mirror of
https://github.com/jellyfin/jellyfin-kodi.git
synced 2025-08-04 20:56:31 +00:00
Version 4.0.0 (#182)
* Adjust refresh behavior * Fix favorites * Add option to mask info * Fix keymap delete * Fix empty show * Version bump 3.1.14 * Reset rescan flag * Fix subtitles encoding * Fix path verification * Fix update library Plug in remove library percentage * Fix unauthorized behavior Reprompt user with login * Fix series pooling * Version bump 3.1.15 * Fix for additional users Return all users, not just public users * Fix http potential errors Prevent from going further if {server} or {userid} is requested but not filled to avoid 401 errors * Fix extra fanart * Fix patch make a case insensitive search * Version bump 3.1.16 Additional logging, fix kodi source. * Fix library tags on update * Version bump 3.1.17 * Fix season artwork * Fix season artwork * Fix logging * Fix blank files sources * Add backup option * Fix userdata song * Transfer data.txt to data.json Use default port for webserver caching * Fix mixed content shortcut * Fix path encoding for patch Hopefully this works... * Fix source nonetype error Just incase, wrap in a try/except because it's not important. * Base fast sync on server time Try to fix music video refresh to prevent cursor from moving up. * Prep subfolders for dynamic Support homevideos for now * Fix empty artist, missing Title * Version bump 3.1.18a * Version bump for objects 171076013 * Notify user of large updates Give option to back out if the user wants to manually update the libraries * Fix sources.xml verification * Prevent error in monitor Put in place try/except in case data is None * Remember sync position for manual triggers Allow to resume sync on restart for manual user triggers (update, repair). Automatically refresh boxsets if movie library is selected. use waitForAbort and emby_should_stop prop to terminate threads * Update string for sync later * Add subfolders for dynamic movies * Small fixes * Version bump 3.1.19 * Fix fast sync try/except, default back to previous behavior. * Fix artwork * Change settings name To ensure it takes default value instead of previous value set in 3.0.34 * Fix transcode flac and live tv * Fix episodes for series pooling * Add live tv support * Version bump 3.1.20 * Revert "Small fixes" This reverts commit9ec1fa3585
. * Version bump 3.1.21 * Fix playback starting server connection instance * Fix show update * Fix boxsets * Fix lastplayed * Patch to support pre 3.6 libraries * Fix slowness * Plug in settings for threading * Plug in settings for threading * Adjust sleep behavior * Version bump 3.1.22 * Fix server detection in monitor * Version bump 3.1.23 * Fix potential error with checksum * Fix missing new artists * Fix library sync Adjust lock, re-add screensaver deactivated during sync, prep compare sync, stop library updates from being processed before startup sync is completed * Version bump 3.1.25 * Fix local trailers * Adjust lock modification * Check db version * Prevent error from creating nodes The addon automatically creates nodes at startup with prefilled information. Prevent errors in the event something goes wrong. It will fix itself down the line, after user has logged in. * Version bump 3.1.26 * Revert "Version bump 3.1.26" This reverts commitc583a69a4b
. * Fix screensaver toggle * Fix source selection for direct stream * Version bump 3.1.26 * Add progress for updates * Revise progress bar Fix typos and subsetting * content notification * Remove content with update library Now remove irrelevant content as well * Fix slowness * Version bump 3.1.27 * Stop trying to get items if server offline * Fix content type for dynamic music * Fix resume sync Now save progress, unless exited due to path validation * Fix artwork for shortcuts on profile switch * Add force transcode settings * Fix audiobooks back to video type Add shortcuts. Audiobook can't be music type otherwise it break resume behavior and it won't play the right item. Has to be video type. * Update general info To finish, download and installation * Update README.md * Move welcome message to service * Prevent patch loop Try once, then let it go, to avoid locking user in a restart loop * Review library threads * Prep for audiobook transcode Still need to implement universal for audio transcode * Version bump 3.1.28 * Fix emby database locked * Fix regression to welcome message * Version bump 3.1.29 * Adjust playback Allow direct play for http streams * Ensure all threads are terminated correctly * Fix empty results due to error 500 * Fix boxset refresh * Fix resume sync behavior Allow to complete the startup sync in the event user backs out of resume sync * Version bump 3.1.30 * Update patch Move patch from cache to addon_data. No longer need to restart Kodi to apply the first patch. * Fix inital sync leading to fast sync * Fix user settings Due to api change in 3.6.0.55 * krypton update * Adjust for resume settings With .55 the resume setting is set per library. Instead query server to see if the item is played to offer delete * Restart service upon check for updates To reload the new objects module. * Fix update library Only do the compare when user selects update library, also add a restart service option in the add-on settings > advanced * Version bump 3.1.31 * Update dependencies * Update FR translation * Update DE translation * Add translation * Support up next * Small service adjustment * Krypton update to support upnext * Add a verification onwake Somehow, Kodi can trigger OnWake without first trigger OnSleep. * Fix loading if special char in path * Add logging and small fixes Prepare userdata by date modified * Version bump 3.1.32 * Change default behavior of startup dialog In case it is forced closed by Kodi, allow the sync to proceed * Ensure deliveryurl is an actual url * Update README.md * Fix nextup * Fix dynamic widgets * Detect coreelect, etc * Fix progress report Silent RefreshProgress in websocket * Follow emby settings for subtitles * Version bump 3.1.33 * Add Italian translation * Fix playback for server 3.6.0.61 * Version bump 3.1.34a * Add silent catch for errors * Adjust playback progress monitor Only track progress report if the item is an emby item * Fix subtitles not following server settings * Add remove libraries, fix mixed libraries * Fix live tv For now, use transcode since direct play returns a 127.0.0.1 unusable address as the path. * Allow live tv to direct stream * Fix LiveTV * Add setting to sync during playback * Fix updates * Fix encoding error * Add optional rotten tomatoes option * Version bump 3.1.35 * Fix emby connect auth string Was preventing proper device detection when using emby connect, play to, etc. * Add setup RT * Fix audio/sub change Only for addon playback * Add developer mode * Update patch Check for updates + dev = forced grab from github * Fix RT string * Fix patch Allow dev mode to redownload zip * Fix patch ugh sleep!! * Verify patch connection * Version bump 3.1.36 * Fix libraries being wiped Catch errors to prevent false positive * Add dateutil library * Prep convert to local time * Fix string * Prep for multi db version support * Fix service restart * Add shortcut restart addon Add notification * Add database discovery * Ensure previous playback terminated * Update translation New: Polish, Dutch Updated: German, French, Italian * Version bump 3.1.37 * Quick fix for new library dateutil * Catch error for dateutil In the event the server has some weird date that can't be converted * Version bump 3.1.38 * Fix dateutil import * Fix db discovery Ignore emby.db * Version bump 3.1.39 * Add a delay if setup not completed Avoid crash from everything loading at once. * Fix database discovery Add table verification + date modified verification * Container optional playutils * Version bump 3.1.40 * Adjust database discovery Compare loaded vs discovered to avoid loading old databases by accident. * Version bump 3.1.41 * Fix discovery toggle * Version bump 3.1.42 * Add webservice for playback prep * Fix service restart * Version bump 3.1.43 * Update default sync indicator Based on overall feedback * Fix check update * Fix if server is selected but unavailable * Support songs without albums * Fix encode and params * Increase retry timeout * Fix update generating duplicates * Add manage libraries Too many entries * Fix database discovery * Fixed transcode via context menu * Fix context transcode * Quiet webservice * Update Krypton objects * Fix database discovery prompt * fixed video listitem issues for krypton * load all item details for playlists * Fix playlist * Version bump 3.1.44 * Fix force hi10p transcoding behavior Fixes the "Force Hi10p transcoding" option to only apply to h264 video codecs * Clear playlist on player.onstop * Don't clear playlist if busy spinner is active * Fix case sensitive issue at calling the log function * fix db stuff (#164) * Reload objects upon initial setup * Fix database discovery ignore db-journal * Update translation German, Italian * Use LastConnectionMode for server test * Fix compare sync * Version bump 3.1.45 * Ensure widgets get updated Container.Refresh alone doesn't seem to work * Update database discovery * Re-add texture to database discovery * Add option to enable/disable service * Remove unused strings * Fix object reload upon restart service * Update Krypton objects * Update translation Dutch, Polish * Version bump 3.1.46 * Adjust client api * Adjust subtitles behavior * Fix string typo * Only run one full sync instance Prevent user from launching multiple syncs and freezing the add-on. * added "playlists" to wnodes * Disable Audiobooks Server doesn't have a set structure yet. This feature is broken atm. * Version bump 4.0.0 * License GPL v3 * Update readme
This commit is contained in:
parent
b8613e1e66
commit
bab67ddf9b
349 changed files with 67373 additions and 21636 deletions
|
@ -1,5 +1,12 @@
|
|||
# Dummy file to make this directory a package.
|
||||
version = "171076028"
|
||||
|
||||
from movies import Movies
|
||||
from musicvideos import MusicVideos
|
||||
from tvshows import TVShows
|
||||
from music import Music
|
||||
from obj import Objects
|
||||
from actions import Actions
|
||||
from actions import PlaylistWorker
|
||||
from actions import on_play, on_update, special_listener
|
||||
|
||||
Objects().mapping()
|
||||
|
|
|
@ -1,207 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
##################################################################################################
|
||||
|
||||
import logging
|
||||
import os
|
||||
import sqlite3
|
||||
|
||||
import xbmc
|
||||
import xbmcvfs
|
||||
|
||||
import api
|
||||
import artwork
|
||||
import downloadutils
|
||||
import read_embyserver as embyserver
|
||||
from ga_client import GoogleAnalytics
|
||||
from utils import window, settings, dialog, language as lang, should_stop
|
||||
|
||||
##################################################################################################
|
||||
|
||||
log = logging.getLogger("EMBY."+__name__)
|
||||
ga = GoogleAnalytics()
|
||||
|
||||
##################################################################################################
|
||||
|
||||
def catch_except(errors=(Exception, ), default_value=False):
|
||||
# Will wrap method with try/except and print parameters for easier debugging
|
||||
def decorator(func):
|
||||
def wrapper(*args, **kwargs):
|
||||
try:
|
||||
return func(*args, **kwargs)
|
||||
except sqlite3.Error as error:
|
||||
raise
|
||||
except errors as error:
|
||||
if not (hasattr(error, 'quiet') and error.quiet):
|
||||
errStrings = ga.formatException()
|
||||
ga.sendEventData("Exception", errStrings[0], errStrings[1], True)
|
||||
log.exception(error)
|
||||
log.error("function: %s \n args: %s \n kwargs: %s",
|
||||
func.__name__, args, kwargs)
|
||||
return default_value
|
||||
|
||||
return wrapper
|
||||
return decorator
|
||||
|
||||
|
||||
class Items(object):
|
||||
|
||||
pdialog = None
|
||||
title = None
|
||||
count = 0
|
||||
total = 0
|
||||
|
||||
|
||||
def __init__(self):
|
||||
|
||||
self.artwork = artwork.Artwork()
|
||||
self.emby = embyserver.Read_EmbyServer()
|
||||
self.do_url = downloadutils.DownloadUtils().downloadUrl
|
||||
self.should_stop = should_stop
|
||||
|
||||
self.kodi_version = int(xbmc.getInfoLabel('System.BuildVersion')[:2])
|
||||
self.direct_path = settings('useDirectPaths') == "1"
|
||||
self.content_msg = settings('newContent') == "true"
|
||||
|
||||
@classmethod
|
||||
def path_validation(cls, path):
|
||||
# Verify if direct path is accessible or not
|
||||
verify_path = path
|
||||
if not os.path.supports_unicode_filenames:
|
||||
verify_path = path.encode('utf-8')
|
||||
|
||||
if window('emby_pathverified') != "true" and not xbmcvfs.exists(verify_path):
|
||||
if dialog(type_="yesno",
|
||||
heading="{emby}",
|
||||
line1="%s %s. %s" % (lang(33047), path, lang(33048))):
|
||||
|
||||
window('emby_shouldStop', value="true")
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def content_pop(self, name):
|
||||
# It's possible for the time to be 0. It should be considered disabled in this case.
|
||||
if not self.pdialog and self.content_msg and self.new_time:
|
||||
dialog(type_="notification",
|
||||
heading="{emby}",
|
||||
message="%s %s" % (lang(33049), name),
|
||||
icon="{emby}",
|
||||
time=self.new_time,
|
||||
sound=False)
|
||||
|
||||
def update_pdialog(self):
|
||||
|
||||
if self.pdialog:
|
||||
percentage = int((float(self.count) / float(self.total))*100)
|
||||
self.pdialog.update(percentage, message=self.title)
|
||||
|
||||
def add_all(self, item_type, items, view=None):
|
||||
|
||||
if self.should_stop():
|
||||
return False
|
||||
|
||||
total = items['TotalRecordCount'] if 'TotalRecordCount' in items else len(items)
|
||||
items = items['Items'] if 'Items' in items else items
|
||||
|
||||
if self.pdialog and view:
|
||||
self.pdialog.update(heading="Processing %s / %s items" % (view['name'], total))
|
||||
|
||||
process = self._get_func(item_type, "added")
|
||||
if view:
|
||||
process(items, total, view)
|
||||
else:
|
||||
process(items, total)
|
||||
|
||||
def process_all(self, item_type, action, items, total=None, view=None):
|
||||
|
||||
log.debug("Processing %s: %s", action, items)
|
||||
|
||||
process = self._get_func(item_type, action)
|
||||
self.total = total or len(items)
|
||||
self.count = 0
|
||||
|
||||
for item in items:
|
||||
|
||||
if self.should_stop():
|
||||
return False
|
||||
|
||||
if not process:
|
||||
continue
|
||||
|
||||
self.title = item.get('Name', "unknown")
|
||||
self.update_pdialog()
|
||||
|
||||
process(item)
|
||||
self.count += 1
|
||||
|
||||
def remove_all(self, item_type, items):
|
||||
|
||||
log.debug("Processing removal: %s", items)
|
||||
|
||||
process = self._get_func(item_type, "remove")
|
||||
for item in items:
|
||||
process(item)
|
||||
|
||||
def added(self, items, total=None, update=True):
|
||||
# Generator for newly added content
|
||||
if update:
|
||||
self.total = total or len(items)
|
||||
self.count = 0
|
||||
|
||||
for item in items:
|
||||
|
||||
if self.should_stop():
|
||||
break
|
||||
|
||||
self.title = item.get('Name', "unknown")
|
||||
|
||||
yield item
|
||||
self.update_pdialog()
|
||||
|
||||
if update:
|
||||
self.count += 1
|
||||
|
||||
def compare(self, item_type, items, compare_to, view=None):
|
||||
|
||||
view_name = view['name'] if view else item_type
|
||||
|
||||
update_list = self._compare_checksum(items, compare_to)
|
||||
log.info("Update for %s: %s", view_name, update_list)
|
||||
|
||||
if self.should_stop():
|
||||
return False
|
||||
|
||||
emby_items = self.emby.getFullItems(update_list)
|
||||
total = len(update_list)
|
||||
|
||||
if self.pdialog:
|
||||
self.pdialog.update(heading="Processing %s / %s items" % (view_name, total))
|
||||
|
||||
# Process additions and updates
|
||||
if emby_items:
|
||||
self.process_all(item_type, "update", emby_items, total, view)
|
||||
# Process deletes
|
||||
if compare_to:
|
||||
self.remove_all(item_type, compare_to.keys())
|
||||
|
||||
return True
|
||||
|
||||
def _compare_checksum(self, items, compare_to):
|
||||
|
||||
update_list = list()
|
||||
|
||||
for item in items:
|
||||
|
||||
if self.should_stop():
|
||||
break
|
||||
|
||||
item_id = item['Id']
|
||||
|
||||
if compare_to.get(item_id) != api.API(item).get_checksum():
|
||||
# Only update if item is not in Kodi or checksum is different
|
||||
update_list.append(item_id)
|
||||
|
||||
compare_to.pop(item_id, None)
|
||||
|
||||
return update_list
|
|
@ -1,813 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
##################################################################################################
|
||||
|
||||
import logging
|
||||
|
||||
import xbmc
|
||||
|
||||
import artwork
|
||||
|
||||
##################################################################################################
|
||||
|
||||
log = logging.getLogger("EMBY."+__name__)
|
||||
|
||||
##################################################################################################
|
||||
|
||||
|
||||
class KodiItems(object):
|
||||
|
||||
|
||||
def __init__(self):
|
||||
|
||||
self.artwork = artwork.Artwork()
|
||||
self.kodi_version = int(xbmc.getInfoLabel('System.BuildVersion')[:2])
|
||||
|
||||
def create_entry_path(self):
|
||||
self.cursor.execute("select coalesce(max(idPath),0) from path")
|
||||
kodi_id = self.cursor.fetchone()[0] + 1
|
||||
|
||||
return kodi_id
|
||||
|
||||
def create_entry_file(self):
|
||||
self.cursor.execute("select coalesce(max(idFile),0) from files")
|
||||
kodi_id = self.cursor.fetchone()[0] + 1
|
||||
|
||||
return kodi_id
|
||||
|
||||
def create_entry_person(self):
|
||||
self.cursor.execute("select coalesce(max(actor_id),0) from actor")
|
||||
kodi_id = self.cursor.fetchone()[0] + 1
|
||||
|
||||
return kodi_id
|
||||
|
||||
def create_entry_genre(self):
|
||||
self.cursor.execute("select coalesce(max(genre_id),0) from genre")
|
||||
kodi_id = self.cursor.fetchone()[0] + 1
|
||||
|
||||
return kodi_id
|
||||
|
||||
def create_entry_studio(self):
|
||||
self.cursor.execute("select coalesce(max(studio_id),0) from studio")
|
||||
kodi_id = self.cursor.fetchone()[0] + 1
|
||||
|
||||
return kodi_id
|
||||
|
||||
def create_entry_bookmark(self):
|
||||
self.cursor.execute("select coalesce(max(idBookmark),0) from bookmark")
|
||||
kodi_id = self.cursor.fetchone()[0] + 1
|
||||
|
||||
return kodi_id
|
||||
|
||||
def create_entry_tag(self):
|
||||
self.cursor.execute("select coalesce(max(tag_id),0) from tag")
|
||||
kodi_id = self.cursor.fetchone()[0] + 1
|
||||
|
||||
return kodi_id
|
||||
|
||||
def add_path(self, path):
|
||||
|
||||
path_id = self.get_path(path)
|
||||
if path_id is None:
|
||||
# Create a new entry
|
||||
path_id = self.create_entry_path()
|
||||
query = (
|
||||
'''
|
||||
INSERT INTO path(idPath, strPath)
|
||||
|
||||
VALUES (?, ?)
|
||||
'''
|
||||
)
|
||||
self.cursor.execute(query, (path_id, path))
|
||||
|
||||
return path_id
|
||||
|
||||
def get_path(self, path):
|
||||
|
||||
query = ' '.join((
|
||||
|
||||
"SELECT idPath",
|
||||
"FROM path",
|
||||
"WHERE strPath = ?"
|
||||
))
|
||||
self.cursor.execute(query, (path,))
|
||||
try:
|
||||
path_id = self.cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
path_id = None
|
||||
|
||||
return path_id
|
||||
|
||||
def update_path(self, path_id, path, media_type, scraper):
|
||||
|
||||
query = ' '.join((
|
||||
|
||||
"UPDATE path",
|
||||
"SET strPath = ?, strContent = ?, strScraper = ?, noUpdate = ?",
|
||||
"WHERE idPath = ?"
|
||||
))
|
||||
self.cursor.execute(query, (path, media_type, scraper, 1, path_id))
|
||||
|
||||
def remove_path(self, path_id):
|
||||
self.cursor.execute("DELETE FROM path WHERE idPath = ?", (path_id,))
|
||||
|
||||
def add_file(self, filename, path_id):
|
||||
|
||||
query = ' '.join((
|
||||
|
||||
"SELECT idFile",
|
||||
"FROM files",
|
||||
"WHERE strFilename = ?",
|
||||
"AND idPath = ?"
|
||||
))
|
||||
self.cursor.execute(query, (filename, path_id,))
|
||||
try:
|
||||
file_id = self.cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
# Create a new entry
|
||||
file_id = self.create_entry_file()
|
||||
query = (
|
||||
'''
|
||||
INSERT INTO files(idFile, idPath, strFilename)
|
||||
|
||||
VALUES (?, ?, ?)
|
||||
'''
|
||||
)
|
||||
self.cursor.execute(query, (file_id, path_id, filename))
|
||||
|
||||
return file_id
|
||||
|
||||
def update_file(self, file_id, filename, path_id, date_added):
|
||||
|
||||
query = ' '.join((
|
||||
|
||||
"UPDATE files",
|
||||
"SET idPath = ?, strFilename = ?, dateAdded = ?",
|
||||
"WHERE idFile = ?"
|
||||
))
|
||||
self.cursor.execute(query, (path_id, filename, date_added, file_id))
|
||||
|
||||
def remove_file(self, path, filename):
|
||||
|
||||
path_id = self.get_path(path)
|
||||
if path_id is not None:
|
||||
|
||||
query = ' '.join((
|
||||
|
||||
"DELETE FROM files",
|
||||
"WHERE idPath = ?",
|
||||
"AND strFilename = ?"
|
||||
))
|
||||
self.cursor.execute(query, (path_id, filename,))
|
||||
|
||||
def get_filename(self, file_id):
|
||||
|
||||
query = ' '.join((
|
||||
|
||||
"SELECT strFilename",
|
||||
"FROM files",
|
||||
"WHERE idFile = ?"
|
||||
))
|
||||
self.cursor.execute(query, (file_id,))
|
||||
try:
|
||||
filename = self.cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
filename = ""
|
||||
|
||||
return filename
|
||||
|
||||
def add_people(self, kodi_id, people, media_type):
|
||||
|
||||
def add_thumbnail(person_id, person, type_):
|
||||
|
||||
thumbnail = person['imageurl']
|
||||
if thumbnail:
|
||||
|
||||
art = type_.lower()
|
||||
if "writing" in art:
|
||||
art = "writer"
|
||||
|
||||
self.artwork.add_update_art(thumbnail, person_id, art, "thumb", self.cursor)
|
||||
|
||||
def add_link(link_type, person_id, kodi_id, media_type):
|
||||
|
||||
query = (
|
||||
"INSERT OR REPLACE INTO " + link_type + "(actor_id, media_id, media_type)"
|
||||
"VALUES (?, ?, ?)"
|
||||
)
|
||||
self.cursor.execute(query, (person_id, kodi_id, media_type))
|
||||
|
||||
cast_order = 1
|
||||
|
||||
if self.kodi_version > 14:
|
||||
|
||||
for person in people:
|
||||
|
||||
name = person['Name']
|
||||
type_ = person['Type']
|
||||
person_id = self._get_person(name)
|
||||
|
||||
# Link person to content
|
||||
if type_ == "Actor":
|
||||
role = person.get('Role')
|
||||
query = (
|
||||
'''
|
||||
INSERT OR REPLACE INTO actor_link(
|
||||
actor_id, media_id, media_type, role, cast_order)
|
||||
|
||||
VALUES (?, ?, ?, ?, ?)
|
||||
'''
|
||||
)
|
||||
self.cursor.execute(query, (person_id, kodi_id, media_type, role, cast_order))
|
||||
cast_order += 1
|
||||
|
||||
elif type_ == "Director":
|
||||
add_link("director_link", person_id, kodi_id, media_type)
|
||||
|
||||
elif type_ in ("Writing", "Writer"):
|
||||
add_link("writer_link", person_id, kodi_id, media_type)
|
||||
|
||||
elif type_ == "Artist":
|
||||
add_link("actor_link", person_id, kodi_id, media_type)
|
||||
|
||||
add_thumbnail(person_id, person, type_)
|
||||
else:
|
||||
# TODO: Remove Helix code when Krypton is RC
|
||||
for person in people:
|
||||
name = person['Name']
|
||||
type_ = person['Type']
|
||||
|
||||
query = ' '.join((
|
||||
|
||||
"SELECT idActor",
|
||||
"FROM actors",
|
||||
"WHERE strActor = ?",
|
||||
"COLLATE NOCASE"
|
||||
))
|
||||
self.cursor.execute(query, (name,))
|
||||
|
||||
try:
|
||||
person_id = self.cursor.fetchone()[0]
|
||||
|
||||
except TypeError:
|
||||
# Cast entry does not exists
|
||||
self.cursor.execute("select coalesce(max(idActor),0) from actors")
|
||||
person_id = self.cursor.fetchone()[0] + 1
|
||||
|
||||
query = "INSERT INTO actors(idActor, strActor) values(?, ?)"
|
||||
self.cursor.execute(query, (person_id, name))
|
||||
log.debug("Add people to media, processing: %s", name)
|
||||
|
||||
finally:
|
||||
# Link person to content
|
||||
if type_ == "Actor":
|
||||
role = person.get('Role')
|
||||
|
||||
if media_type == "movie":
|
||||
query = (
|
||||
'''
|
||||
INSERT OR REPLACE INTO actorlinkmovie(
|
||||
idActor, idMovie, strRole, iOrder)
|
||||
|
||||
VALUES (?, ?, ?, ?)
|
||||
'''
|
||||
)
|
||||
elif media_type == "tvshow":
|
||||
query = (
|
||||
'''
|
||||
INSERT OR REPLACE INTO actorlinktvshow(
|
||||
idActor, idShow, strRole, iOrder)
|
||||
|
||||
VALUES (?, ?, ?, ?)
|
||||
'''
|
||||
)
|
||||
elif media_type == "episode":
|
||||
query = (
|
||||
'''
|
||||
INSERT OR REPLACE INTO actorlinkepisode(
|
||||
idActor, idEpisode, strRole, iOrder)
|
||||
|
||||
VALUES (?, ?, ?, ?)
|
||||
'''
|
||||
)
|
||||
else: return # Item is invalid
|
||||
|
||||
self.cursor.execute(query, (person_id, kodi_id, role, cast_order))
|
||||
cast_order += 1
|
||||
|
||||
elif type_ == "Director":
|
||||
if media_type == "movie":
|
||||
query = (
|
||||
'''
|
||||
INSERT OR REPLACE INTO directorlinkmovie(idDirector, idMovie)
|
||||
VALUES (?, ?)
|
||||
'''
|
||||
)
|
||||
elif media_type == "tvshow":
|
||||
query = (
|
||||
'''
|
||||
INSERT OR REPLACE INTO directorlinktvshow(idDirector, idShow)
|
||||
VALUES (?, ?)
|
||||
'''
|
||||
)
|
||||
elif media_type == "musicvideo":
|
||||
query = (
|
||||
'''
|
||||
INSERT OR REPLACE INTO directorlinkmusicvideo(idDirector, idMVideo)
|
||||
VALUES (?, ?)
|
||||
'''
|
||||
)
|
||||
elif media_type == "episode":
|
||||
query = (
|
||||
'''
|
||||
INSERT OR REPLACE INTO directorlinkepisode(idDirector, idEpisode)
|
||||
VALUES (?, ?)
|
||||
'''
|
||||
)
|
||||
else: return # Item is invalid
|
||||
|
||||
self.cursor.execute(query, (person_id, kodi_id))
|
||||
|
||||
elif type_ in ("Writing", "Writer"):
|
||||
if media_type == "movie":
|
||||
query = (
|
||||
'''
|
||||
INSERT OR REPLACE INTO writerlinkmovie(idWriter, idMovie)
|
||||
VALUES (?, ?)
|
||||
'''
|
||||
)
|
||||
elif media_type == "episode":
|
||||
query = (
|
||||
'''
|
||||
INSERT OR REPLACE INTO writerlinkepisode(idWriter, idEpisode)
|
||||
VALUES (?, ?)
|
||||
'''
|
||||
)
|
||||
else: return # Item is invalid
|
||||
|
||||
self.cursor.execute(query, (person_id, kodi_id))
|
||||
|
||||
elif type_ == "Artist":
|
||||
query = (
|
||||
'''
|
||||
INSERT OR REPLACE INTO artistlinkmusicvideo(idArtist, idMVideo)
|
||||
VALUES (?, ?)
|
||||
'''
|
||||
)
|
||||
self.cursor.execute(query, (person_id, kodi_id))
|
||||
|
||||
add_thumbnail(person_id, person, type_)
|
||||
|
||||
def _add_person(self, name):
|
||||
|
||||
person_id = self.create_entry_person()
|
||||
query = "INSERT INTO actor(actor_id, name) values(?, ?)"
|
||||
self.cursor.execute(query, (person_id, name))
|
||||
log.debug("Add people to media, processing: %s", name)
|
||||
|
||||
return person_id
|
||||
|
||||
def _get_person(self, name):
|
||||
|
||||
query = ' '.join((
|
||||
|
||||
"SELECT actor_id",
|
||||
"FROM actor",
|
||||
"WHERE name = ?",
|
||||
"COLLATE NOCASE"
|
||||
))
|
||||
self.cursor.execute(query, (name,))
|
||||
|
||||
try:
|
||||
person_id = self.cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
person_id = self._add_person(name)
|
||||
|
||||
return person_id
|
||||
|
||||
def add_genres(self, kodi_id, genres, media_type):
|
||||
|
||||
if self.kodi_version > 14:
|
||||
# Delete current genres for clean slate
|
||||
query = ' '.join((
|
||||
|
||||
"DELETE FROM genre_link",
|
||||
"WHERE media_id = ?",
|
||||
"AND media_type = ?"
|
||||
))
|
||||
self.cursor.execute(query, (kodi_id, media_type,))
|
||||
|
||||
# Add genres
|
||||
for genre in genres:
|
||||
|
||||
genre_id = self._get_genre(genre)
|
||||
query = (
|
||||
'''
|
||||
INSERT OR REPLACE INTO genre_link(
|
||||
genre_id, media_id, media_type)
|
||||
|
||||
VALUES (?, ?, ?)
|
||||
'''
|
||||
)
|
||||
self.cursor.execute(query, (genre_id, kodi_id, media_type))
|
||||
else:
|
||||
# TODO: Remove Helix code when Krypton is RC
|
||||
# Delete current genres for clean slate
|
||||
if media_type == "movie":
|
||||
self.cursor.execute("DELETE FROM genrelinkmovie WHERE idMovie = ?", (kodi_id,))
|
||||
elif media_type == "tvshow":
|
||||
self.cursor.execute("DELETE FROM genrelinktvshow WHERE idShow = ?", (kodi_id,))
|
||||
elif media_type == "musicvideo":
|
||||
self.cursor.execute("DELETE FROM genrelinkmusicvideo WHERE idMVideo = ?", (kodi_id,))
|
||||
|
||||
# Add genres
|
||||
for genre in genres:
|
||||
|
||||
query = ' '.join((
|
||||
|
||||
"SELECT idGenre",
|
||||
"FROM genre",
|
||||
"WHERE strGenre = ?",
|
||||
"COLLATE NOCASE"
|
||||
))
|
||||
self.cursor.execute(query, (genre,))
|
||||
|
||||
try:
|
||||
genre_id = self.cursor.fetchone()[0]
|
||||
|
||||
except TypeError:
|
||||
# Create genre in database
|
||||
self.cursor.execute("select coalesce(max(idGenre),0) from genre")
|
||||
genre_id = self.cursor.fetchone()[0] + 1
|
||||
|
||||
query = "INSERT INTO genre(idGenre, strGenre) values(?, ?)"
|
||||
self.cursor.execute(query, (genre_id, genre))
|
||||
log.debug("Add Genres to media, processing: %s", genre)
|
||||
|
||||
finally:
|
||||
# Assign genre to item
|
||||
if media_type == "movie":
|
||||
query = (
|
||||
'''
|
||||
INSERT OR REPLACE into genrelinkmovie(idGenre, idMovie)
|
||||
VALUES (?, ?)
|
||||
'''
|
||||
)
|
||||
elif media_type == "tvshow":
|
||||
query = (
|
||||
'''
|
||||
INSERT OR REPLACE into genrelinktvshow(idGenre, idShow)
|
||||
VALUES (?, ?)
|
||||
'''
|
||||
)
|
||||
elif media_type == "musicvideo":
|
||||
query = (
|
||||
'''
|
||||
INSERT OR REPLACE into genrelinkmusicvideo(idGenre, idMVideo)
|
||||
VALUES (?, ?)
|
||||
'''
|
||||
)
|
||||
else: return # Item is invalid
|
||||
|
||||
self.cursor.execute(query, (genre_id, kodi_id))
|
||||
|
||||
def _add_genre(self, genre):
|
||||
|
||||
genre_id = self.create_entry_genre()
|
||||
query = "INSERT INTO genre(genre_id, name) values(?, ?)"
|
||||
self.cursor.execute(query, (genre_id, genre))
|
||||
log.debug("Add Genres to media, processing: %s", genre)
|
||||
|
||||
return genre_id
|
||||
|
||||
def _get_genre(self, genre):
|
||||
|
||||
query = ' '.join((
|
||||
|
||||
"SELECT genre_id",
|
||||
"FROM genre",
|
||||
"WHERE name = ?",
|
||||
"COLLATE NOCASE"
|
||||
))
|
||||
self.cursor.execute(query, (genre,))
|
||||
|
||||
try:
|
||||
genre_id = self.cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
genre_id = self._add_genre(genre)
|
||||
|
||||
return genre_id
|
||||
|
||||
def add_studios(self, kodi_id, studios, media_type):
|
||||
|
||||
if self.kodi_version > 14:
|
||||
|
||||
for studio in studios:
|
||||
|
||||
studio_id = self._get_studio(studio)
|
||||
query = (
|
||||
'''
|
||||
INSERT OR REPLACE INTO studio_link(studio_id, media_id, media_type)
|
||||
VALUES (?, ?, ?)
|
||||
''')
|
||||
self.cursor.execute(query, (studio_id, kodi_id, media_type))
|
||||
else:
|
||||
# TODO: Remove Helix code when Krypton is RC
|
||||
for studio in studios:
|
||||
|
||||
query = ' '.join((
|
||||
|
||||
"SELECT idstudio",
|
||||
"FROM studio",
|
||||
"WHERE strstudio = ?",
|
||||
"COLLATE NOCASE"
|
||||
))
|
||||
self.cursor.execute(query, (studio,))
|
||||
try:
|
||||
studio_id = self.cursor.fetchone()[0]
|
||||
|
||||
except TypeError:
|
||||
# Studio does not exists.
|
||||
self.cursor.execute("select coalesce(max(idstudio),0) from studio")
|
||||
studio_id = self.cursor.fetchone()[0] + 1
|
||||
|
||||
query = "INSERT INTO studio(idstudio, strstudio) values(?, ?)"
|
||||
self.cursor.execute(query, (studio_id, studio))
|
||||
log.debug("Add Studios to media, processing: %s", studio)
|
||||
|
||||
finally: # Assign studio to item
|
||||
if media_type == "movie":
|
||||
query = (
|
||||
'''
|
||||
INSERT OR REPLACE INTO studiolinkmovie(idstudio, idMovie)
|
||||
VALUES (?, ?)
|
||||
''')
|
||||
elif media_type == "musicvideo":
|
||||
query = (
|
||||
'''
|
||||
INSERT OR REPLACE INTO studiolinkmusicvideo(idstudio, idMVideo)
|
||||
VALUES (?, ?)
|
||||
''')
|
||||
elif media_type == "tvshow":
|
||||
query = (
|
||||
'''
|
||||
INSERT OR REPLACE INTO studiolinktvshow(idstudio, idShow)
|
||||
VALUES (?, ?)
|
||||
''')
|
||||
elif media_type == "episode":
|
||||
query = (
|
||||
'''
|
||||
INSERT OR REPLACE INTO studiolinkepisode(idstudio, idEpisode)
|
||||
VALUES (?, ?)
|
||||
''')
|
||||
self.cursor.execute(query, (studio_id, kodi_id))
|
||||
|
||||
def _add_studio(self, studio):
|
||||
|
||||
studio_id = self.create_entry_studio()
|
||||
query = "INSERT INTO studio(studio_id, name) values(?, ?)"
|
||||
self.cursor.execute(query, (studio_id, studio))
|
||||
log.debug("Add Studios to media, processing: %s", studio)
|
||||
|
||||
return studio_id
|
||||
|
||||
def _get_studio(self, studio):
|
||||
|
||||
query = ' '.join((
|
||||
|
||||
"SELECT studio_id",
|
||||
"FROM studio",
|
||||
"WHERE name = ?",
|
||||
"COLLATE NOCASE"
|
||||
))
|
||||
self.cursor.execute(query, (studio,))
|
||||
try:
|
||||
studio_id = self.cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
studio_id = self._add_studio(studio)
|
||||
|
||||
return studio_id
|
||||
|
||||
def add_streams(self, file_id, streams, runtime):
|
||||
# First remove any existing entries
|
||||
self.cursor.execute("DELETE FROM streamdetails WHERE idFile = ?", (file_id,))
|
||||
if streams:
|
||||
# Video details
|
||||
for track in streams['video']:
|
||||
query = (
|
||||
'''
|
||||
INSERT INTO streamdetails(
|
||||
idFile, iStreamType, strVideoCodec, fVideoAspect,
|
||||
iVideoWidth, iVideoHeight, iVideoDuration ,strStereoMode)
|
||||
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
||||
'''
|
||||
)
|
||||
self.cursor.execute(query, (file_id, 0, track['codec'], track['aspect'],
|
||||
track['width'], track['height'], runtime,
|
||||
track['video3DFormat']))
|
||||
# Audio details
|
||||
for track in streams['audio']:
|
||||
query = (
|
||||
'''
|
||||
INSERT INTO streamdetails(
|
||||
idFile, iStreamType, strAudioCodec, iAudioChannels, strAudioLanguage)
|
||||
|
||||
VALUES (?, ?, ?, ?, ?)
|
||||
'''
|
||||
)
|
||||
self.cursor.execute(query, (file_id, 1, track['codec'], track['channels'],
|
||||
track['language']))
|
||||
# Subtitles details
|
||||
for track in streams['subtitle']:
|
||||
query = (
|
||||
'''
|
||||
INSERT INTO streamdetails(idFile, iStreamType, strSubtitleLanguage)
|
||||
VALUES (?, ?, ?)
|
||||
'''
|
||||
)
|
||||
self.cursor.execute(query, (file_id, 2, track))
|
||||
|
||||
def add_playstate(self, file_id, resume, total, playcount, date_played):
|
||||
|
||||
# Delete existing resume point
|
||||
self.cursor.execute("DELETE FROM bookmark WHERE idFile = ?", (file_id,))
|
||||
# Set watched count
|
||||
self.set_playcount(file_id, playcount, date_played)
|
||||
|
||||
if resume:
|
||||
bookmark_id = self.create_entry_bookmark()
|
||||
query = (
|
||||
'''
|
||||
INSERT INTO bookmark(
|
||||
idBookmark, idFile, timeInSeconds, totalTimeInSeconds, player, type)
|
||||
|
||||
VALUES (?, ?, ?, ?, ?, ?)
|
||||
'''
|
||||
)
|
||||
self.cursor.execute(query, (bookmark_id, file_id, resume, total, "DVDPlayer", 1))
|
||||
|
||||
def set_playcount(self, file_id, playcount, date_played):
|
||||
|
||||
query = ' '.join((
|
||||
|
||||
"UPDATE files",
|
||||
"SET playCount = ?, lastPlayed = ?",
|
||||
"WHERE idFile = ?"
|
||||
))
|
||||
self.cursor.execute(query, (playcount, date_played, file_id))
|
||||
|
||||
def add_tags(self, kodi_id, tags, media_type):
|
||||
|
||||
if self.kodi_version > 14:
|
||||
|
||||
query = ' '.join((
|
||||
|
||||
"DELETE FROM tag_link",
|
||||
"WHERE media_id = ?",
|
||||
"AND media_type = ?"
|
||||
))
|
||||
self.cursor.execute(query, (kodi_id, media_type))
|
||||
|
||||
# Add tags
|
||||
log.debug("Adding Tags: %s", tags)
|
||||
for tag in tags:
|
||||
tag_id = self.get_tag(kodi_id, tag, media_type)
|
||||
else:
|
||||
# TODO: Remove Helix code when Krypton is RC
|
||||
query = ' '.join((
|
||||
|
||||
"DELETE FROM taglinks",
|
||||
"WHERE idMedia = ?",
|
||||
"AND media_type = ?"
|
||||
))
|
||||
self.cursor.execute(query, (kodi_id, media_type))
|
||||
|
||||
# Add tags
|
||||
log.debug("Adding Tags: %s", tags)
|
||||
for tag in tags:
|
||||
tag_id = self.get_tag_old(kodi_id, tag, media_type)
|
||||
|
||||
def _add_tag(self, tag):
|
||||
|
||||
tag_id = self.create_entry_tag()
|
||||
query = "INSERT INTO tag(tag_id, name) values(?, ?)"
|
||||
self.cursor.execute(query, (tag_id, tag))
|
||||
log.debug("Create tag_id: %s name: %s", tag_id, tag)
|
||||
|
||||
return tag_id
|
||||
|
||||
def get_tag(self, kodi_id, tag, media_type):
|
||||
|
||||
if self.kodi_version > 14:
|
||||
|
||||
query = ' '.join((
|
||||
|
||||
"SELECT tag_id",
|
||||
"FROM tag",
|
||||
"WHERE name = ?",
|
||||
"COLLATE NOCASE"
|
||||
))
|
||||
self.cursor.execute(query, (tag,))
|
||||
try:
|
||||
tag_id = self.cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
tag_id = self._add_tag(tag)
|
||||
|
||||
query = (
|
||||
'''
|
||||
INSERT OR REPLACE INTO tag_link(tag_id, media_id, media_type)
|
||||
VALUES (?, ?, ?)
|
||||
'''
|
||||
)
|
||||
self.cursor.execute(query, (tag_id, kodi_id, media_type))
|
||||
else:
|
||||
# TODO: Remove Helix code when Krypton is RC
|
||||
tag_id = self.get_tag_old(kodi_id, tag, media_type)
|
||||
|
||||
return tag_id
|
||||
|
||||
def get_tag_old(self, kodi_id, tag, media_type):
|
||||
# TODO: Remove Helix code when Krypton is RC
|
||||
query = ' '.join((
|
||||
|
||||
"SELECT idTag",
|
||||
"FROM tag",
|
||||
"WHERE strTag = ?",
|
||||
"COLLATE NOCASE"
|
||||
))
|
||||
self.cursor.execute(query, (tag,))
|
||||
try:
|
||||
tag_id = self.cursor.fetchone()[0]
|
||||
|
||||
except TypeError:
|
||||
# Create the tag
|
||||
self.cursor.execute("select coalesce(max(idTag),0) from tag")
|
||||
tag_id = self.cursor.fetchone()[0] + 1
|
||||
|
||||
query = "INSERT INTO tag(idTag, strTag) values(?, ?)"
|
||||
self.cursor.execute(query, (tag_id, tag))
|
||||
log.debug("Create idTag: %s name: %s", tag_id, tag)
|
||||
|
||||
finally:
|
||||
# Assign tag to item
|
||||
query = (
|
||||
'''
|
||||
INSERT OR REPLACE INTO taglinks(
|
||||
idTag, idMedia, media_type)
|
||||
|
||||
VALUES (?, ?, ?)
|
||||
'''
|
||||
)
|
||||
self.cursor.execute(query, (tag_id, kodi_id, media_type))
|
||||
|
||||
return tag_id
|
||||
|
||||
def remove_tag(self, kodi_id, tag, media_type):
|
||||
|
||||
if self.kodi_version > 14:
|
||||
|
||||
query = ' '.join((
|
||||
|
||||
"SELECT tag_id",
|
||||
"FROM tag",
|
||||
"WHERE name = ?",
|
||||
"COLLATE NOCASE"
|
||||
))
|
||||
self.cursor.execute(query, (tag,))
|
||||
try:
|
||||
tag_id = self.cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
return
|
||||
else:
|
||||
query = ' '.join((
|
||||
|
||||
"DELETE FROM tag_link",
|
||||
"WHERE media_id = ?",
|
||||
"AND media_type = ?",
|
||||
"AND tag_id = ?"
|
||||
))
|
||||
self.cursor.execute(query, (kodi_id, media_type, tag_id,))
|
||||
else:
|
||||
# TODO: Remove Helix code when Krypton is RC
|
||||
query = ' '.join((
|
||||
|
||||
"SELECT idTag",
|
||||
"FROM tag",
|
||||
"WHERE strTag = ?",
|
||||
"COLLATE NOCASE"
|
||||
))
|
||||
self.cursor.execute(query, (tag,))
|
||||
try:
|
||||
tag_id = self.cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
return
|
||||
else:
|
||||
query = ' '.join((
|
||||
|
||||
"DELETE FROM taglinks",
|
||||
"WHERE idMedia = ?",
|
||||
"AND media_type = ?",
|
||||
"AND idTag = ?"
|
||||
))
|
||||
self.cursor.execute(query, (kodi_id, media_type, tag_id,))
|
|
@ -1,301 +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):
|
||||
query = (
|
||||
'''
|
||||
INSERT INTO movie(
|
||||
idMovie, idFile, c00, c01, c02, c03, c04, c05, c06, c07,
|
||||
c09, c10, c11, c12, c14, c15, c16, c18, c19, c21)
|
||||
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
'''
|
||||
)
|
||||
self.cursor.execute(query, (args))
|
||||
|
||||
def add_movie_17(self, *args):
|
||||
# Create the movie entry
|
||||
query = (
|
||||
'''
|
||||
INSERT INTO movie(
|
||||
idMovie, idFile, c00, c01, c02, c03, c04, c05, c06, c07,
|
||||
c09, c10, c11, c12, c14, c15, c16, c18, c19, c21, premiered)
|
||||
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
'''
|
||||
)
|
||||
self.cursor.execute(query, (args))
|
||||
|
||||
def update_movie(self, *args):
|
||||
query = ' '.join((
|
||||
|
||||
"UPDATE movie",
|
||||
"SET c00 = ?, c01 = ?, c02 = ?, c03 = ?, c04 = ?, c05 = ?, c06 = ?,",
|
||||
"c07 = ?, c09 = ?, c10 = ?, c11 = ?, c12 = ?, c14 = ?, c15 = ?,",
|
||||
"c16 = ?, c18 = ?, c19 = ?, c21 = ?",
|
||||
"WHERE idMovie = ?"
|
||||
))
|
||||
self.cursor.execute(query, (args))
|
||||
|
||||
def update_movie_17(self, *args):
|
||||
query = ' '.join((
|
||||
|
||||
"UPDATE movie",
|
||||
"SET c00 = ?, c01 = ?, c02 = ?, c03 = ?, c04 = ?, c05 = ?, c06 = ?,",
|
||||
"c07 = ?, c09 = ?, c10 = ?, c11 = ?, c12 = ?, c14 = ?, c15 = ?,",
|
||||
"c16 = ?, c18 = ?, c19 = ?, c21 = ?, premiered = ?",
|
||||
"WHERE idMovie = ?"
|
||||
))
|
||||
self.cursor.execute(query, (args))
|
||||
|
||||
def remove_movie(self, kodi_id, file_id):
|
||||
self.cursor.execute("DELETE FROM movie WHERE idMovie = ?", (kodi_id,))
|
||||
self.cursor.execute("DELETE FROM files WHERE idFile = ?", (file_id,))
|
||||
|
||||
def get_ratingid(self, media_id):
|
||||
|
||||
query = "SELECT rating_id FROM rating WHERE media_id = ?"
|
||||
self.cursor.execute(query, (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_id = ?"
|
||||
self.cursor.execute(query, (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):
|
||||
|
||||
if self.kodi_version > 14:
|
||||
|
||||
for country in countries:
|
||||
country_id = self._get_country(country)
|
||||
|
||||
query = (
|
||||
'''
|
||||
INSERT OR REPLACE INTO country_link(country_id, media_id, media_type)
|
||||
VALUES (?, ?, ?)
|
||||
'''
|
||||
)
|
||||
self.cursor.execute(query, (country_id, kodi_id, "movie"))
|
||||
else:
|
||||
# TODO: Remove Helix code when Krypton is RC
|
||||
for country in countries:
|
||||
query = ' '.join((
|
||||
|
||||
"SELECT idCountry",
|
||||
"FROM country",
|
||||
"WHERE strCountry = ?",
|
||||
"COLLATE NOCASE"
|
||||
))
|
||||
self.cursor.execute(query, (country,))
|
||||
|
||||
try:
|
||||
country_id = self.cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
# Create a new entry
|
||||
self.cursor.execute("select coalesce(max(idCountry),0) from country")
|
||||
country_id = self.cursor.fetchone()[0] + 1
|
||||
|
||||
query = "INSERT INTO country(idCountry, strCountry) values(?, ?)"
|
||||
self.cursor.execute(query, (country_id, country))
|
||||
log.debug("Add country to media, processing: %s", country)
|
||||
|
||||
query = (
|
||||
'''
|
||||
INSERT OR REPLACE INTO countrylinkmovie(idCountry, idMovie)
|
||||
VALUES (?, ?)
|
||||
'''
|
||||
)
|
||||
self.cursor.execute(query, (country_id, kodi_id))
|
||||
|
||||
def _add_country(self, country):
|
||||
|
||||
country_id = self.create_entry_country()
|
||||
query = "INSERT INTO country(country_id, name) values(?, ?)"
|
||||
self.cursor.execute(query, (country_id, country))
|
||||
log.debug("Add country to media, processing: %s", country)
|
||||
|
||||
return country_id
|
||||
|
||||
def _get_country(self, country):
|
||||
query = ' '.join((
|
||||
|
||||
"SELECT country_id",
|
||||
"FROM country",
|
||||
"WHERE name = ?",
|
||||
"COLLATE NOCASE"
|
||||
))
|
||||
self.cursor.execute(query, (country,))
|
||||
try:
|
||||
country_id = self.cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
country_id = self._add_country(country)
|
||||
|
||||
return country_id
|
||||
|
||||
def add_boxset(self, boxset):
|
||||
query = ' '.join((
|
||||
|
||||
"SELECT idSet",
|
||||
"FROM sets",
|
||||
"WHERE strSet = ?",
|
||||
"COLLATE NOCASE"
|
||||
))
|
||||
self.cursor.execute(query, (boxset,))
|
||||
try:
|
||||
set_id = self.cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
set_id = self._add_boxset(boxset)
|
||||
|
||||
return set_id
|
||||
|
||||
def _add_boxset(self, boxset):
|
||||
|
||||
set_id = self.create_entry_set()
|
||||
query = "INSERT INTO sets(idSet, strSet) values(?, ?)"
|
||||
self.cursor.execute(query, (set_id, boxset))
|
||||
log.debug("Adding boxset: %s", boxset)
|
||||
|
||||
return set_id
|
||||
|
||||
def update_boxset(self, set_id, boxset):
|
||||
query = ' '.join((
|
||||
|
||||
"UPDATE sets",
|
||||
"SET strSet = ?",
|
||||
"WHERE idSet = ?"
|
||||
))
|
||||
self.cursor.execute(query, (boxset, set_id,))
|
||||
|
||||
def set_boxset(self, set_id, movie_id):
|
||||
query = ' '.join((
|
||||
|
||||
"UPDATE movie",
|
||||
"SET idSet = ?",
|
||||
"WHERE idMovie = ?"
|
||||
))
|
||||
self.cursor.execute(query, (set_id, movie_id,))
|
||||
|
||||
def remove_from_boxset(self, movie_id):
|
||||
query = ' '.join((
|
||||
|
||||
"UPDATE movie",
|
||||
"SET idSet = null",
|
||||
"WHERE idMovie = ?"
|
||||
))
|
||||
self.cursor.execute(query, (movie_id,))
|
||||
|
||||
def remove_boxset(self, kodi_id):
|
||||
self.cursor.execute("DELETE FROM sets WHERE idSet = ?", (kodi_id,))
|
|
@ -1,406 +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):
|
||||
self.cursor.execute("select coalesce(max(idArtist),0) from artist")
|
||||
kodi_id = self.cursor.fetchone()[0] + 1
|
||||
|
||||
return kodi_id
|
||||
|
||||
def create_entry_album(self):
|
||||
self.cursor.execute("select coalesce(max(idAlbum),0) from album")
|
||||
kodi_id = self.cursor.fetchone()[0] + 1
|
||||
|
||||
return kodi_id
|
||||
|
||||
def create_entry_song(self):
|
||||
self.cursor.execute("select coalesce(max(idSong),0) from song")
|
||||
kodi_id = self.cursor.fetchone()[0] + 1
|
||||
|
||||
return kodi_id
|
||||
|
||||
def create_entry_genre(self):
|
||||
self.cursor.execute("select coalesce(max(idGenre),0) from genre")
|
||||
kodi_id = self.cursor.fetchone()[0] + 1
|
||||
|
||||
return kodi_id
|
||||
|
||||
def update_path(self, path_id, path):
|
||||
|
||||
query = "UPDATE path SET strPath = ? WHERE idPath = ?"
|
||||
self.cursor.execute(query, (path, path_id))
|
||||
|
||||
def add_role(self):
|
||||
query = (
|
||||
'''
|
||||
INSERT OR REPLACE INTO role(idRole, strRole)
|
||||
VALUES (?, ?)
|
||||
'''
|
||||
)
|
||||
self.cursor.execute(query, (1, 'Composer'))
|
||||
|
||||
def get_artist(self, name, musicbrainz):
|
||||
|
||||
query = ' '.join((
|
||||
|
||||
"SELECT idArtist, strArtist",
|
||||
"FROM artist",
|
||||
"WHERE strMusicBrainzArtistID = ?"
|
||||
))
|
||||
self.cursor.execute(query, (musicbrainz,))
|
||||
try:
|
||||
result = self.cursor.fetchone()
|
||||
artist_id = result[0]
|
||||
artist_name = result[1]
|
||||
except TypeError:
|
||||
artist_id = self._add_artist(name, musicbrainz)
|
||||
else:
|
||||
if artist_name != name:
|
||||
self.update_artist_name(artist_id, name)
|
||||
|
||||
return artist_id
|
||||
|
||||
def _add_artist(self, name, musicbrainz):
|
||||
|
||||
query = ' '.join((
|
||||
# Safety check, when musicbrainz does not exist
|
||||
"SELECT idArtist",
|
||||
"FROM artist",
|
||||
"WHERE strArtist = ?",
|
||||
"COLLATE NOCASE"
|
||||
))
|
||||
self.cursor.execute(query, (name,))
|
||||
try:
|
||||
artist_id = self.cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
artist_id = self.create_entry()
|
||||
query = (
|
||||
'''
|
||||
INSERT INTO artist(idArtist, strArtist, strMusicBrainzArtistID)
|
||||
VALUES (?, ?, ?)
|
||||
'''
|
||||
)
|
||||
self.cursor.execute(query, (artist_id, name, musicbrainz))
|
||||
|
||||
return artist_id
|
||||
|
||||
def update_artist_name(self, kodi_id, name):
|
||||
|
||||
query = "UPDATE artist SET strArtist = ? WHERE idArtist = ?"
|
||||
self.cursor.execute(query, (name, kodi_id,))
|
||||
|
||||
def update_artist_16(self, *args):
|
||||
query = ' '.join((
|
||||
|
||||
"UPDATE artist",
|
||||
"SET strGenres = ?, strBiography = ?, strImage = ?, strFanart = ?,",
|
||||
"lastScraped = ?",
|
||||
"WHERE idArtist = ?"
|
||||
))
|
||||
self.cursor.execute(query, (args))
|
||||
|
||||
def update_artist(self, *args):
|
||||
query = ' '.join((
|
||||
|
||||
"UPDATE artist",
|
||||
"SET strGenres = ?, strBiography = ?, strImage = ?, strFanart = ?,",
|
||||
"lastScraped = ?, dateAdded = ?",
|
||||
"WHERE idArtist = ?"
|
||||
))
|
||||
self.cursor.execute(query, (args))
|
||||
|
||||
def link_artist(self, kodi_id, album_id, name):
|
||||
query = (
|
||||
'''
|
||||
INSERT OR REPLACE INTO album_artist(idArtist, idAlbum, strArtist)
|
||||
VALUES (?, ?, ?)
|
||||
'''
|
||||
)
|
||||
self.cursor.execute(query, (kodi_id, album_id, name))
|
||||
|
||||
def add_discography(self, kodi_id, album, year):
|
||||
query = (
|
||||
'''
|
||||
INSERT OR REPLACE INTO discography(idArtist, strAlbum, strYear)
|
||||
VALUES (?, ?, ?)
|
||||
'''
|
||||
)
|
||||
self.cursor.execute(query, (kodi_id, album, year))
|
||||
|
||||
def get_album(self, name, musicbrainz):
|
||||
|
||||
query = ' '.join((
|
||||
|
||||
"SELECT idAlbum",
|
||||
"FROM album",
|
||||
"WHERE strMusicBrainzAlbumID = ?"
|
||||
))
|
||||
self.cursor.execute(query, (musicbrainz,))
|
||||
try:
|
||||
album_id = self.cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
album_id = self._add_album(name, musicbrainz)
|
||||
|
||||
return album_id
|
||||
|
||||
def _add_album(self, name, musicbrainz):
|
||||
|
||||
album_id = self.create_entry_album()
|
||||
if self.kodi_version > 14:
|
||||
query = (
|
||||
'''
|
||||
INSERT INTO album(idAlbum, strAlbum, strMusicBrainzAlbumID, strReleaseType)
|
||||
VALUES (?, ?, ?, ?)
|
||||
'''
|
||||
)
|
||||
self.cursor.execute(query, (album_id, name, musicbrainz, "album"))
|
||||
else:
|
||||
# TODO: Remove Helix code when Krypton is RC
|
||||
query = (
|
||||
'''
|
||||
INSERT INTO album(idAlbum, strAlbum, strMusicBrainzAlbumID)
|
||||
VALUES (?, ?, ?)
|
||||
'''
|
||||
)
|
||||
self.cursor.execute(query, (album_id, name, musicbrainz))
|
||||
|
||||
return album_id
|
||||
|
||||
def update_album(self, *args):
|
||||
query = ' '.join((
|
||||
|
||||
"UPDATE album",
|
||||
"SET strArtists = ?, iYear = ?, strGenres = ?, strReview = ?, strImage = ?,",
|
||||
"iRating = ?, lastScraped = ?, strReleaseType = ?",
|
||||
"WHERE idAlbum = ?"
|
||||
))
|
||||
self.cursor.execute(query, (args))
|
||||
|
||||
def update_album_17(self, *args):
|
||||
query = ' '.join((
|
||||
|
||||
"UPDATE album",
|
||||
"SET strArtists = ?, iYear = ?, strGenres = ?, strReview = ?, strImage = ?,",
|
||||
"iUserrating = ?, lastScraped = ?, strReleaseType = ?",
|
||||
"WHERE idAlbum = ?"
|
||||
))
|
||||
self.cursor.execute(query, (args))
|
||||
|
||||
def update_album_15(self, *args):
|
||||
query = ' '.join((
|
||||
|
||||
"UPDATE album",
|
||||
"SET strArtists = ?, iYear = ?, strGenres = ?, strReview = ?, strImage = ?,",
|
||||
"iRating = ?, lastScraped = ?, dateAdded = ?, strReleaseType = ?",
|
||||
"WHERE idAlbum = ?"
|
||||
))
|
||||
self.cursor.execute(query, (args))
|
||||
|
||||
def update_album_14(self, *args):
|
||||
# TODO: Remove Helix code when Krypton is RC
|
||||
query = ' '.join((
|
||||
|
||||
"UPDATE album",
|
||||
"SET strArtists = ?, iYear = ?, strGenres = ?, strReview = ?, strImage = ?,",
|
||||
"iRating = ?, lastScraped = ?, dateAdded = ?",
|
||||
"WHERE idAlbum = ?"
|
||||
))
|
||||
self.cursor.execute(query, (args))
|
||||
|
||||
def get_album_artist(self, album_id, artists):
|
||||
|
||||
query = ' '.join((
|
||||
|
||||
"SELECT strArtists",
|
||||
"FROM album",
|
||||
"WHERE idAlbum = ?"
|
||||
))
|
||||
self.cursor.execute(query, (album_id,))
|
||||
try:
|
||||
curr_artists = self.cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
return
|
||||
|
||||
if curr_artists != artists:
|
||||
self._update_album_artist(album_id, artists)
|
||||
|
||||
def _update_album_artist(self, album_id, artists):
|
||||
|
||||
query = "UPDATE album SET strArtists = ? WHERE idAlbum = ?"
|
||||
self.cursor.execute(query, (artists, album_id))
|
||||
|
||||
def add_single(self, *args):
|
||||
query = (
|
||||
'''
|
||||
INSERT INTO album(idAlbum, strGenres, iYear, strReleaseType)
|
||||
|
||||
VALUES (?, ?, ?, ?)
|
||||
'''
|
||||
)
|
||||
self.cursor.execute(query, (args))
|
||||
|
||||
def add_single_15(self, *args):
|
||||
query = (
|
||||
'''
|
||||
INSERT INTO album(idAlbum, strGenres, iYear, dateAdded, strReleaseType)
|
||||
|
||||
VALUES (?, ?, ?, ?, ?)
|
||||
'''
|
||||
)
|
||||
self.cursor.execute(query, (args))
|
||||
|
||||
def add_single_14(self, *args):
|
||||
# TODO: Remove Helix code when Krypton is RC
|
||||
query = (
|
||||
'''
|
||||
INSERT INTO album(idAlbum, strGenres, iYear, dateAdded)
|
||||
|
||||
VALUES (?, ?, ?, ?)
|
||||
'''
|
||||
)
|
||||
self.cursor.execute(query, (args))
|
||||
|
||||
def add_song(self, *args):
|
||||
query = (
|
||||
'''
|
||||
INSERT INTO song(
|
||||
idSong, idAlbum, idPath, strArtists, strGenres, strTitle, iTrack,
|
||||
iDuration, iYear, strFileName, strMusicBrainzTrackID, iTimesPlayed, lastplayed,
|
||||
rating)
|
||||
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
'''
|
||||
)
|
||||
self.cursor.execute(query, (args))
|
||||
|
||||
def update_song(self, *args):
|
||||
query = ' '.join((
|
||||
|
||||
"UPDATE song",
|
||||
"SET idAlbum = ?, strArtists = ?, strGenres = ?, strTitle = ?, iTrack = ?,",
|
||||
"iDuration = ?, iYear = ?, strFilename = ?, iTimesPlayed = ?, lastplayed = ?,",
|
||||
"rating = ?, comment = ?",
|
||||
"WHERE idSong = ?"
|
||||
))
|
||||
self.cursor.execute(query, (args))
|
||||
|
||||
def link_song_artist(self, kodi_id, song_id, index, artist):
|
||||
|
||||
if self.kodi_version > 16:
|
||||
query = (
|
||||
'''
|
||||
INSERT OR REPLACE INTO song_artist(idArtist, idSong, idRole, iOrder, strArtist)
|
||||
VALUES (?, ?, ?, ?, ?)
|
||||
'''
|
||||
)
|
||||
self.cursor.execute(query, (kodi_id, song_id, 1, index, artist))
|
||||
else:
|
||||
query = (
|
||||
'''
|
||||
INSERT OR REPLACE INTO song_artist(idArtist, idSong, iOrder, strArtist)
|
||||
VALUES (?, ?, ?, ?)
|
||||
'''
|
||||
)
|
||||
self.cursor.execute(query, (kodi_id, song_id, index, artist))
|
||||
|
||||
def link_song_album(self, song_id, album_id, track, title, duration):
|
||||
query = (
|
||||
'''
|
||||
INSERT OR REPLACE INTO albuminfosong(
|
||||
idAlbumInfoSong, idAlbumInfo, iTrack, strTitle, iDuration)
|
||||
|
||||
VALUES (?, ?, ?, ?, ?)
|
||||
'''
|
||||
)
|
||||
self.cursor.execute(query, (song_id, album_id, track, title, duration))
|
||||
|
||||
def rate_song(self, kodi_id, playcount, rating, date_played):
|
||||
|
||||
query = "UPDATE song SET iTimesPlayed = ?, lastplayed = ?, rating = ? WHERE idSong = ?"
|
||||
self.cursor.execute(query, (playcount, date_played, rating, kodi_id))
|
||||
|
||||
def add_genres(self, kodi_id, genres, media_type):
|
||||
|
||||
if media_type == "album":
|
||||
# Delete current genres for clean slate
|
||||
query = ' '.join((
|
||||
|
||||
"DELETE FROM album_genre",
|
||||
"WHERE idAlbum = ?"
|
||||
))
|
||||
self.cursor.execute(query, (kodi_id,))
|
||||
|
||||
for genre in genres:
|
||||
|
||||
genre_id = self.get_genre(genre)
|
||||
query = "INSERT OR REPLACE INTO album_genre(idGenre, idAlbum) values(?, ?)"
|
||||
self.cursor.execute(query, (genre_id, kodi_id))
|
||||
|
||||
elif media_type == "song":
|
||||
# Delete current genres for clean slate
|
||||
query = ' '.join((
|
||||
|
||||
"DELETE FROM song_genre",
|
||||
"WHERE idSong = ?"
|
||||
))
|
||||
self.cursor.execute(query, (kodi_id,))
|
||||
|
||||
for genre in genres:
|
||||
|
||||
genre_id = self.get_genre(genre)
|
||||
query = "INSERT OR REPLACE INTO song_genre(idGenre, idSong) values(?, ?)"
|
||||
self.cursor.execute(query, (genre_id, kodi_id))
|
||||
|
||||
def get_genre(self, genre):
|
||||
|
||||
query = ' '.join((
|
||||
|
||||
"SELECT idGenre",
|
||||
"FROM genre",
|
||||
"WHERE strGenre = ?",
|
||||
"COLLATE NOCASE"
|
||||
))
|
||||
self.cursor.execute(query, (genre,))
|
||||
try:
|
||||
genre_id = self.cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
genre_id = self._add_genre(genre)
|
||||
|
||||
return genre_id
|
||||
|
||||
def _add_genre(self, genre):
|
||||
|
||||
genre_id = self.create_entry_genre()
|
||||
query = "INSERT INTO genre(idGenre, strGenre) values(?, ?)"
|
||||
self.cursor.execute(query, (genre_id, genre))
|
||||
|
||||
return genre_id
|
||||
|
||||
def remove_artist(self, kodi_id):
|
||||
self.cursor.execute("DELETE FROM artist WHERE idArtist = ?", (kodi_id,))
|
||||
|
||||
def remove_album(self, kodi_id):
|
||||
self.cursor.execute("DELETE FROM album WHERE idAlbum = ?", (kodi_id,))
|
||||
|
||||
def remove_song(self, kodi_id):
|
||||
self.cursor.execute("DELETE FROM song WHERE idSong = ?", (kodi_id,))
|
|
@ -1,66 +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)
|
||||
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
'''
|
||||
)
|
||||
self.cursor.execute(query, (args))
|
||||
|
||||
def update_musicvideo(self, *args):
|
||||
|
||||
query = ' '.join((
|
||||
|
||||
"UPDATE musicvideo",
|
||||
"SET c00 = ?, c04 = ?, c05 = ?, c06 = ?, c07 = ?, c08 = ?, c09 = ?, c10 = ?,",
|
||||
"c11 = ?, c12 = ?"
|
||||
"WHERE idMVideo = ?"
|
||||
))
|
||||
self.cursor.execute(query, (args))
|
||||
|
||||
def remove_musicvideo(self, kodi_id, file_id):
|
||||
self.cursor.execute("DELETE FROM musicvideo WHERE idMVideo = ?", (kodi_id,))
|
||||
self.cursor.execute("DELETE FROM files WHERE idFile = ?", (file_id,))
|
|
@ -1,245 +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_id):
|
||||
|
||||
query = "SELECT rating_id FROM rating WHERE media_id = ?"
|
||||
self.cursor.execute(query, (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_id = ?"
|
||||
self.cursor.execute(query, (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 self.kodi_version > 15 and name is not None:
|
||||
query = "UPDATE seasons SET name = ? WHERE idSeason = ?"
|
||||
self.cursor.execute(query, (name, season_id))
|
||||
|
||||
return season_id
|
||||
|
||||
def _add_season(self, show_id, number):
|
||||
|
||||
season_id = self.create_entry_season()
|
||||
query = "INSERT INTO seasons(idSeason, idShow, season) values(?, ?, ?)"
|
||||
self.cursor.execute(query, (season_id, show_id, number))
|
||||
|
||||
return season_id
|
||||
|
||||
def add_episode(self, *args):
|
||||
query = (
|
||||
'''
|
||||
INSERT INTO episode(
|
||||
idEpisode, idFile, c00, c01, c03, c04, c05, c09, c10, c12, c13, c14,
|
||||
idShow, c15, c16)
|
||||
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
'''
|
||||
)
|
||||
self.cursor.execute(query, (args))
|
||||
|
||||
def add_episode_16(self, *args):
|
||||
query = (
|
||||
'''
|
||||
INSERT INTO episode(
|
||||
idEpisode, idFile, c00, c01, c03, c04, c05, c09, c10, c12, c13, c14,
|
||||
idShow, c15, c16, idSeason)
|
||||
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
'''
|
||||
)
|
||||
self.cursor.execute(query, (args))
|
||||
|
||||
def update_episode(self, *args):
|
||||
query = ' '.join((
|
||||
|
||||
"UPDATE episode",
|
||||
"SET c00 = ?, c01 = ?, c03 = ?, c04 = ?, c05 = ?, c09 = ?, c10 = ?,",
|
||||
"c12 = ?, c13 = ?, c14 = ?, c15 = ?, c16 = ?, idShow = ?",
|
||||
"WHERE idEpisode = ?"
|
||||
))
|
||||
self.cursor.execute(query, (args))
|
||||
|
||||
def update_episode_16(self, *args):
|
||||
query = ' '.join((
|
||||
|
||||
"UPDATE episode",
|
||||
"SET c00 = ?, c01 = ?, c03 = ?, c04 = ?, c05 = ?, c09 = ?, c10 = ?,",
|
||||
"c12 = ?, c13 = ?, c14 = ?, c15 = ?, c16 = ?, idSeason = ?, idShow = ?",
|
||||
"WHERE idEpisode = ?"
|
||||
))
|
||||
self.cursor.execute(query, (args))
|
||||
|
||||
def remove_tvshow(self, kodi_id):
|
||||
self.cursor.execute("DELETE FROM tvshow WHERE idShow = ?", (kodi_id,))
|
||||
|
||||
def remove_season(self, kodi_id):
|
||||
self.cursor.execute("DELETE FROM seasons WHERE idSeason = ?", (kodi_id,))
|
||||
|
||||
def remove_episode(self, kodi_id, file_id):
|
||||
self.cursor.execute("DELETE FROM episode WHERE idEpisode = ?", (kodi_id,))
|
||||
self.cursor.execute("DELETE FROM files WHERE idFile = ?", (file_id,))
|
814
resources/lib/objects/actions.py
Normal file
814
resources/lib/objects/actions.py
Normal file
|
@ -0,0 +1,814 @@
|
|||
# -*- 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, playlist=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'])
|
||||
|
||||
transcode = transcode or settings('playFromTranscode.bool')
|
||||
kodi_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(kodi_playlist.getposition(), 0) + 1 # Can return -1
|
||||
force_play = False
|
||||
|
||||
self.stack[0][1].setPath(self.stack[0][0])
|
||||
try:
|
||||
if not playlist and 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:
|
||||
|
||||
kodi_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(kodi_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['MediaType'] in ('Video', 'Audio'):
|
||||
resume = item['UserData'].get('PlaybackPositionTicks')
|
||||
|
||||
if resume:
|
||||
if get_play_action() == "Resume":
|
||||
seektime = True
|
||||
|
||||
if transcode and not seektime:
|
||||
choice = self.resume_dialog(api.API(item, self.server).adjust_resume((resume or 0) / 10000000.0))
|
||||
|
||||
if choice is None:
|
||||
raise Exception("User backed out of resume dialog.")
|
||||
|
||||
seektime = False if not choice else True
|
||||
|
||||
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, intro=True)
|
||||
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. Add additional items as plugin listing.
|
||||
'''
|
||||
item = items['Items'][0]
|
||||
playlist = self.get_playlist(item)
|
||||
player = xbmc.Player()
|
||||
|
||||
#xbmc.executebuiltin("Playlist.Clear") # Clear playlist to remove the previous item from playlist position no.2
|
||||
|
||||
if clear:
|
||||
if player.isPlaying():
|
||||
player.stop()
|
||||
|
||||
xbmc.executebuiltin('ActivateWindow(busydialognocancel)')
|
||||
index = 0
|
||||
else:
|
||||
index = max(playlist.getposition(), 0) + 1 # Can return -1
|
||||
|
||||
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)
|
||||
|
||||
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 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 clear:
|
||||
xbmc.executebuiltin('Dialog.Close(busydialognocancel)')
|
||||
player.play(playlist)
|
||||
|
||||
for item in items['Items'][1:]:
|
||||
listitem = xbmcgui.ListItem()
|
||||
LOG.info("[ playlist/%s ] %s", item['Id'], item['Name'])
|
||||
|
||||
self.set_listitem(item, listitem, None, False)
|
||||
path = "plugin://plugin.video.emby/?mode=play&id=%s&playlist=true" % item['Id']
|
||||
listitem.setPath(path)
|
||||
|
||||
playlist.add(path, listitem, index)
|
||||
index += 1
|
||||
|
||||
def set_listitem(self, item, listitem, db_id=None, seektime=None, intro=False):
|
||||
|
||||
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)
|
||||
|
||||
elif item['Type'] in ('TvChannel'):
|
||||
|
||||
obj = objects.map(item, 'BrowseChannel')
|
||||
obj['Artwork'] = API.get_all_artwork(objects.map(item, 'Artwork'))
|
||||
self.listitem_channel(obj, listitem, item)
|
||||
|
||||
else:
|
||||
obj = objects.map(item, 'BrowseVideo')
|
||||
obj['DbId'] = db_id
|
||||
obj['Artwork'] = API.get_all_artwork(objects.map(item, 'ArtworkParent'), True)
|
||||
|
||||
if intro:
|
||||
obj['Artwork']['Primary'] = "&KodiCinemaMode=true"
|
||||
|
||||
self.listitem_video(obj, listitem, item, seektime, intro)
|
||||
|
||||
if 'PlaybackInfo' in item:
|
||||
|
||||
if seektime:
|
||||
item['PlaybackInfo']['CurrentPosition'] = obj['Resume']
|
||||
|
||||
if 'SubtitleUrl' in item['PlaybackInfo']:
|
||||
|
||||
LOG.info("[ subtitles ] %s", item['PlaybackInfo']['SubtitleUrl'])
|
||||
listitem.setSubtitles([item['PlaybackInfo']['SubtitleUrl']])
|
||||
|
||||
if item['Type'] == 'Episode':
|
||||
|
||||
item['PlaybackInfo']['CurrentEpisode'] = objects.map(item, "UpNext")
|
||||
item['PlaybackInfo']['CurrentEpisode']['art'] = {
|
||||
'tvshow.poster': obj['Artwork'].get('Series.Primary'),
|
||||
'thumb': obj['Artwork'].get('Primary'),
|
||||
'tvshow.fanart': None
|
||||
}
|
||||
if obj['Artwork']['Backdrop']:
|
||||
item['PlaybackInfo']['CurrentEpisode']['art']['tvshow.fanart'] = obj['Artwork']['Backdrop'][0]
|
||||
|
||||
listitem.setContentLookup(False)
|
||||
|
||||
def listitem_video(self, obj, listitem, item, seektime=None, intro=False):
|
||||
|
||||
''' Set listitem for video content. That also include streams.
|
||||
'''
|
||||
API = api.API(item, self.server)
|
||||
is_video = obj['MediaType'] in ('Video', 'Audio') # audiobook
|
||||
|
||||
obj['Genres'] = " / ".join(obj['Genres'] or [])
|
||||
obj['Studios'] = [API.validate_studio(studio) for studio in (obj['Studios'] or [])]
|
||||
obj['Studios'] = " / ".join(obj['Studios'])
|
||||
obj['Mpaa'] = API.get_mpaa(obj['Mpaa'])
|
||||
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['ChildCount'] = obj['ChildCount'] or 0
|
||||
obj['RecursiveCount'] = obj['RecursiveCount'] or 0
|
||||
obj['Unwatched'] = obj['Unwatched'] or 0
|
||||
obj['Artwork']['Backdrop'] = obj['Artwork']['Backdrop'] or []
|
||||
obj['Artwork']['Thumb'] = obj['Artwork']['Thumb'] or ""
|
||||
|
||||
if not intro and not obj['Type'] == 'Trailer':
|
||||
obj['Artwork']['Primary'] = obj['Artwork']['Primary'] or "special://home/addons/plugin.video.emby/icon.png"
|
||||
else:
|
||||
obj['Artwork']['Primary'] = obj['Artwork']['Primary'] or obj['Artwork']['Thumb'] or (obj['Artwork']['Backdrop'][0] if len(obj['Artwork']['Backdrop']) else "special://home/addons/plugin.video.emby/fanart.jpg")
|
||||
obj['Artwork']['Primary'] += "&KodiTrailer=true" if obj['Type'] == 'Trailer' else "&KodiCinemaMode=true"
|
||||
obj['Artwork']['Backdrop'] = [obj['Artwork']['Primary']]
|
||||
|
||||
self.set_artwork(obj['Artwork'], listitem, obj['Type'])
|
||||
|
||||
if intro or obj['Type'] == 'Trailer':
|
||||
listitem.setArt({'poster': ""}) # Clear the poster value for intros / trailers to prevent issues in skins
|
||||
|
||||
listitem.setIconImage('DefaultVideo.png')
|
||||
listitem.setThumbnailImage(obj['Artwork']['Primary'])
|
||||
|
||||
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'],
|
||||
'votes': obj['Votes'],
|
||||
'dateadded': obj['DateAdded'],
|
||||
'aired': obj['Year'],
|
||||
'date': obj['FileDate'],
|
||||
'dbid': obj['DbId']
|
||||
}
|
||||
listitem.setCast(API.get_actors())
|
||||
|
||||
if 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'],
|
||||
'aired': obj['Premiere'],
|
||||
})
|
||||
|
||||
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':
|
||||
|
||||
if obj['Status'] != 'Ended':
|
||||
obj['Status'] = None
|
||||
|
||||
metadata.update({
|
||||
'mediatype': "tvshow",
|
||||
'tvshowtitle': obj['Title'],
|
||||
'status': obj['Status']
|
||||
})
|
||||
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'],
|
||||
'userrating': obj['CriticRating']
|
||||
})
|
||||
|
||||
elif obj['Type'] == 'MusicVideo':
|
||||
metadata.update({
|
||||
'mediatype': "musicvideo",
|
||||
'album': obj['Album'],
|
||||
'artist': obj['Artists'] or [],
|
||||
'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'],
|
||||
'year': obj['Year'],
|
||||
'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_channel(self, obj, listitem, item):
|
||||
|
||||
''' Set listitem for channel content.
|
||||
'''
|
||||
API = api.API(item, self.server)
|
||||
|
||||
obj['Title'] = "%s - %s" % (obj['Title'], obj['ProgramName'])
|
||||
obj['Runtime'] = round(float((obj['Runtime'] or 0) / 10000000.0), 6)
|
||||
obj['PlayCount'] = API.get_playcount(obj['Played'], obj['PlayCount']) or 0
|
||||
obj['Overlay'] = 7 if obj['Played'] else 6
|
||||
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"]
|
||||
|
||||
|
||||
metadata = {
|
||||
'title': obj['Title'],
|
||||
'originaltitle': obj['Title'],
|
||||
'playcount': obj['PlayCount'],
|
||||
'overlay': obj['Overlay']
|
||||
}
|
||||
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']})
|
||||
|
||||
listitem.setProperty('totaltime', str(obj['Runtime']))
|
||||
listitem.setProperty('IsPlayable', 'true')
|
||||
listitem.setProperty('IsFolder', 'false')
|
||||
|
||||
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 not obj['Played']:
|
||||
obj['DatePlayed'] = None
|
||||
elif 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'])
|
||||
|
||||
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')
|
||||
listitem.setIconImage('DefaultPicture.png')
|
||||
else:
|
||||
listitem.setProperty('IsFolder', 'true')
|
||||
listitem.setIconImage('DefaultFolder.png')
|
||||
|
||||
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.bool', False)
|
||||
else:
|
||||
LOG.info("Resume dialog: Resume selected.")
|
||||
window('emby.resume.bool', True)
|
||||
|
||||
elif isPlaying and not window('emby.external_check'):
|
||||
time = player.getTime()
|
||||
|
||||
if time > 1: # Not external player.
|
||||
|
||||
window('emby.external_check', value="true")
|
||||
window('emby.external_count', value="0")
|
||||
elif count == 120:
|
||||
|
||||
LOG.info("External player detected.")
|
||||
window('emby.external.bool', True)
|
||||
window('emby.external_check.bool', True)
|
||||
window('emby.external_count', value="0")
|
||||
|
||||
elif time == 0:
|
||||
window('emby.external_count', value=str(count + 1))
|
6
resources/lib/objects/kodi/__init__.py
Normal file
6
resources/lib/objects/kodi/__init__.py
Normal file
|
@ -0,0 +1,6 @@
|
|||
from kodi import Kodi
|
||||
from movies import Movies
|
||||
from musicvideos import MusicVideos
|
||||
from tvshows import TVShows
|
||||
from music import Music
|
||||
from artwork import Artwork
|
386
resources/lib/objects/kodi/artwork.py
Normal file
386
resources/lib/objects/kodi/artwork.py
Normal file
|
@ -0,0 +1,386 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
#################################################################################################
|
||||
|
||||
import logging
|
||||
import urllib
|
||||
import Queue
|
||||
import threading
|
||||
|
||||
import xbmc
|
||||
import xbmcvfs
|
||||
|
||||
import queries as QU
|
||||
import queries_texture as QUTEX
|
||||
from helper import window, settings
|
||||
from libraries import requests
|
||||
|
||||
##################################################################################################
|
||||
|
||||
LOG = logging.getLogger("EMBY."+__name__)
|
||||
|
||||
##################################################################################################
|
||||
|
||||
|
||||
class Artwork(object):
|
||||
|
||||
def __init__(self, cursor):
|
||||
|
||||
self.cursor = cursor
|
||||
self.enable_cache = settings('enableTextureCache.bool')
|
||||
self.queue = Queue.Queue()
|
||||
self.threads = []
|
||||
self.kodi = {
|
||||
'username': settings('webServerUser'),
|
||||
'password': settings('webServerPass'),
|
||||
'host': "localhost",
|
||||
'port': settings('webServerPort')
|
||||
}
|
||||
|
||||
|
||||
def update(self, image_url, kodi_id, media, image):
|
||||
|
||||
''' Update artwork in the video database.
|
||||
Only cache artwork if it changed for the main backdrop, poster.
|
||||
Delete current entry before updating with the new one.
|
||||
Cache fanart and poster in Kodi texture cache.
|
||||
'''
|
||||
if not image_url or image == 'poster' and media in ('song', 'artist', 'album'):
|
||||
return
|
||||
|
||||
cache = False
|
||||
|
||||
try:
|
||||
self.cursor.execute(QU.get_art, (kodi_id, media, image,))
|
||||
url = self.cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
|
||||
cache = True
|
||||
LOG.debug("ADD to kodi_id %s art: %s", kodi_id, image_url)
|
||||
self.cursor.execute(QU.add_art, (kodi_id, media, image, image_url))
|
||||
else:
|
||||
if url != image_url:
|
||||
cache = True
|
||||
|
||||
if image in ('fanart', 'poster'):
|
||||
self.delete_cache(url)
|
||||
|
||||
LOG.info("UPDATE to kodi_id %s art: %s", kodi_id, image_url)
|
||||
self.cursor.execute(QU.update_art, (image_url, kodi_id, media, image))
|
||||
|
||||
if cache and image in ('fanart', 'poster'):
|
||||
self.cache(image_url)
|
||||
|
||||
def add(self, artwork, *args):
|
||||
|
||||
''' Add all artworks.
|
||||
'''
|
||||
KODI = {
|
||||
'Primary': ['thumb', 'poster'],
|
||||
'Banner': "banner",
|
||||
'Logo': "clearlogo",
|
||||
'Art': "clearart",
|
||||
'Thumb': "landscape",
|
||||
'Disc': "discart",
|
||||
'Backdrop': "fanart"
|
||||
}
|
||||
|
||||
for art in KODI:
|
||||
|
||||
if art == 'Backdrop':
|
||||
self.cursor.execute(QU.get_backdrops, args + ("fanart%",))
|
||||
|
||||
if len(self.cursor.fetchall()) > len(artwork['Backdrop']):
|
||||
self.cursor.execute(QU.delete_backdrops, args + ("fanart_",))
|
||||
|
||||
for index, backdrop in enumerate(artwork['Backdrop']):
|
||||
|
||||
if index:
|
||||
self.update(*(backdrop,) + args + ("%s%s" % ("fanart", index),))
|
||||
else:
|
||||
self.update(*(backdrop,) + args + ("fanart",))
|
||||
|
||||
elif art == 'Primary':
|
||||
for kodi_image in KODI['Primary']:
|
||||
self.update(*(artwork['Primary'],) + args + (kodi_image,))
|
||||
|
||||
elif artwork.get(art):
|
||||
self.update(*(artwork[art],) + args + (KODI[art],))
|
||||
|
||||
def delete(self, *args):
|
||||
|
||||
''' Delete artwork from kodi database and remove cache for backdrop/posters.
|
||||
'''
|
||||
self.cursor.execute(QU.get_art_url, args)
|
||||
|
||||
for row in self.cursor.fetchall():
|
||||
if row[1] in ('poster', 'fanart'):
|
||||
self.delete_cache(row[0])
|
||||
|
||||
def cache(self, url):
|
||||
|
||||
''' Cache a single image to texture cache.
|
||||
'''
|
||||
if not url or not self.enable_cache:
|
||||
return
|
||||
|
||||
url = self.double_urlencode(url)
|
||||
self.queue.put(url)
|
||||
self.add_worker()
|
||||
|
||||
def double_urlencode(self, text):
|
||||
|
||||
text = self.single_urlencode(text)
|
||||
text = self.single_urlencode(text)
|
||||
|
||||
return text
|
||||
|
||||
def single_urlencode(self, text):
|
||||
|
||||
''' urlencode needs a utf-string.
|
||||
return the result as unicode
|
||||
'''
|
||||
text = urllib.urlencode({'blahblahblah': text.encode('utf-8')})
|
||||
text = text[13:]
|
||||
|
||||
return text.decode('utf-8')
|
||||
|
||||
def add_worker(self):
|
||||
|
||||
for thread in self.threads:
|
||||
if thread.is_done:
|
||||
self.threads.remove(thread)
|
||||
|
||||
if self.queue.qsize() and len(self.threads) < 2:
|
||||
|
||||
new_thread = GetArtworkWorker(self.kodi, self.queue)
|
||||
new_thread.start()
|
||||
LOG.info("-->[ q:artwork/%s ]", id(new_thread))
|
||||
self.threads.append(new_thread)
|
||||
|
||||
def delete_cache(self, url):
|
||||
|
||||
''' Delete cached artwork.
|
||||
'''
|
||||
from database import Database
|
||||
|
||||
with Database('texture') as texturedb:
|
||||
|
||||
try:
|
||||
texturedb.cursor.execute(QUTEX.get_cache, (url,))
|
||||
cached = texturedb.cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
LOG.debug("Could not find cached url: %s", url)
|
||||
else:
|
||||
thumbnails = xbmc.translatePath("special://thumbnails/%s" % cached).decode('utf-8')
|
||||
xbmcvfs.delete(thumbnails)
|
||||
texturedb.cursor.execute(QUTEX.delete_cache, (url,))
|
||||
LOG.info("DELETE cached %s", cached)
|
||||
|
||||
|
||||
class GetArtworkWorker(threading.Thread):
|
||||
|
||||
is_done = False
|
||||
|
||||
def __init__(self, kodi, queue):
|
||||
|
||||
self.kodi = kodi
|
||||
self.queue = queue
|
||||
threading.Thread.__init__(self)
|
||||
|
||||
def run(self):
|
||||
|
||||
''' Prepare the request. Request removes the urlencode which is required in this case.
|
||||
Use a session allows to use a pool of connections.
|
||||
'''
|
||||
with requests.Session() as s:
|
||||
while True:
|
||||
|
||||
try:
|
||||
url = self.queue.get(timeout=2)
|
||||
except Queue.Empty:
|
||||
|
||||
self.is_done = True
|
||||
LOG.info("--<[ q:artwork/%s ]", id(self))
|
||||
|
||||
return
|
||||
|
||||
try:
|
||||
req = requests.Request(method='HEAD',
|
||||
url="http://%s:%s/image/image://%s" % (self.kodi['host'], self.kodi['port'], url),
|
||||
auth=(self.kodi['username'], self.kodi['password']))
|
||||
prep = req.prepare()
|
||||
prep.url = "http://%s:%s/image/image://%s" % (self.kodi['host'], self.kodi['port'], url)
|
||||
s.send(prep, timeout=(0.01, 0.01))
|
||||
s.content # release the connection
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
self.queue.task_done()
|
||||
|
||||
if xbmc.Monitor().abortRequested():
|
||||
break
|
||||
|
||||
|
||||
|
||||
|
||||
"""
|
||||
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
#################################################################################################
|
||||
|
||||
import logging
|
||||
import os
|
||||
import urllib
|
||||
from sqlite3 import OperationalError
|
||||
|
||||
import xbmc
|
||||
import xbmcgui
|
||||
import xbmcvfs
|
||||
import requests
|
||||
|
||||
import resources.lib.image_cache_thread as image_cache_thread
|
||||
from resources.lib.helper import _, window, settings, JSONRPC
|
||||
from resources.lib.database import Database
|
||||
from __objs__ import QU
|
||||
|
||||
##################################################################################################
|
||||
|
||||
log = logging.getLogger("EMBY."+__name__)
|
||||
|
||||
##################################################################################################
|
||||
|
||||
|
||||
class Artwork(object):
|
||||
|
||||
xbmc_host = 'localhost'
|
||||
xbmc_port = None
|
||||
xbmc_username = None
|
||||
xbmc_password = None
|
||||
|
||||
image_cache_threads = []
|
||||
image_cache_limit = 0
|
||||
|
||||
|
||||
def __init__(self, server):
|
||||
|
||||
self.server = server
|
||||
self.enable_texture_cache = settings('enableTextureCache') == "true"
|
||||
self.image_cache_limit = int(settings('imageCacheLimit')) * 5
|
||||
log.debug("image cache thread count: %s", self.image_cache_limit)
|
||||
|
||||
if not self.xbmc_port and self.enable_texture_cache:
|
||||
self._set_webserver_details()
|
||||
|
||||
|
||||
def texture_cache_sync(self):
|
||||
# This method will sync all Kodi artwork to textures13.db
|
||||
# and cache them locally. This takes diskspace!
|
||||
if not dialog(type_="yesno",
|
||||
heading="{emby}",
|
||||
line1=_(33042)):
|
||||
return
|
||||
|
||||
log.info("Doing Image Cache Sync")
|
||||
|
||||
pdialog = xbmcgui.DialogProgress()
|
||||
pdialog.create(_(29999), _(33043))
|
||||
|
||||
# ask to rest all existing or not
|
||||
if dialog(type_="yesno", heading="{emby}", line1=_(33044)):
|
||||
log.info("Resetting all cache data first")
|
||||
self.delete_cache()
|
||||
|
||||
# Cache all entries in video DB
|
||||
self._cache_all_video_entries(pdialog)
|
||||
# Cache all entries in music DB
|
||||
self._cache_all_music_entries(pdialog)
|
||||
|
||||
pdialog.update(100, "%s %s" % (_(33046), len(self.image_cache_threads)))
|
||||
log.info("Waiting for all threads to exit")
|
||||
|
||||
while len(self.image_cache_threads):
|
||||
for thread in self.image_cache_threads:
|
||||
if thread.is_finished:
|
||||
self.image_cache_threads.remove(thread)
|
||||
pdialog.update(100, "%s %s" % (_(33046), len(self.image_cache_threads)))
|
||||
log.info("Waiting for all threads to exit: %s", len(self.image_cache_threads))
|
||||
xbmc.sleep(500)
|
||||
|
||||
pdialog.close()
|
||||
|
||||
@classmethod
|
||||
def delete_cache(cls):
|
||||
# Remove all existing textures first
|
||||
path = xbmc.translatePath('special://thumbnails/').decode('utf-8')
|
||||
if xbmcvfs.exists(path):
|
||||
dirs, ignore_files = xbmcvfs.listdir(path)
|
||||
for directory in dirs:
|
||||
ignore_dirs, files = xbmcvfs.listdir(path + directory)
|
||||
for file_ in files:
|
||||
|
||||
if os.path.supports_unicode_filenames:
|
||||
filename = os.path.join(path + directory.decode('utf-8'),
|
||||
file_.decode('utf-8'))
|
||||
else:
|
||||
filename = os.path.join(path.encode('utf-8') + directory, file_)
|
||||
|
||||
xbmcvfs.delete(filename)
|
||||
log.debug("deleted: %s", filename)
|
||||
|
||||
# remove all existing data from texture DB
|
||||
with DatabaseConn('texture') as cursor_texture:
|
||||
cursor_texture.execute('SELECT tbl_name FROM sqlite_master WHERE type="table"')
|
||||
rows = cursor_texture.fetchall()
|
||||
for row in rows:
|
||||
table_name = row[0]
|
||||
if table_name != "version":
|
||||
cursor_texture.execute("DELETE FROM " + table_name)
|
||||
|
||||
def _cache_all_video_entries(self, pdialog):
|
||||
|
||||
with Database('video') as cursor_video:
|
||||
|
||||
cursor_video.execute("SELECT url FROM art WHERE media_type != 'actor'") # dont include actors
|
||||
result = cursor_video.fetchall()
|
||||
total = len(result)
|
||||
log.info("Image cache sync about to process %s images", total)
|
||||
cursor_video.close()
|
||||
|
||||
count = 0
|
||||
for url in result:
|
||||
|
||||
if pdialog.iscanceled():
|
||||
break
|
||||
|
||||
percentage = int((float(count) / float(total))*100)
|
||||
message = "%s of %s (%s)" % (count, total, len(self.image_cache_threads))
|
||||
pdialog.update(percentage, "%s %s" % (_(33045), message))
|
||||
self.cache_texture(url[0])
|
||||
count += 1
|
||||
|
||||
def _cache_all_music_entries(self, pdialog):
|
||||
|
||||
with Database('music') as cursor_music:
|
||||
|
||||
cursor_music.execute("SELECT url FROM art")
|
||||
result = cursor_music.fetchall()
|
||||
total = len(result)
|
||||
|
||||
log.info("Image cache sync about to process %s images", total)
|
||||
|
||||
count = 0
|
||||
for url in result:
|
||||
|
||||
if pdialog.iscanceled():
|
||||
break
|
||||
|
||||
percentage = int((float(count) / float(total))*100)
|
||||
message = "%s of %s" % (count, total)
|
||||
pdialog.update(percentage, "%s %s" % (_(33045), message))
|
||||
self.cache_texture(url[0])
|
||||
count += 1
|
||||
|
||||
"""
|
||||
|
297
resources/lib/objects/kodi/kodi.py
Normal file
297
resources/lib/objects/kodi/kodi.py
Normal file
|
@ -0,0 +1,297 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
##################################################################################################
|
||||
|
||||
import logging
|
||||
|
||||
import xbmc
|
||||
|
||||
import artwork
|
||||
import queries as QU
|
||||
from helper import values
|
||||
|
||||
##################################################################################################
|
||||
|
||||
LOG = logging.getLogger("EMBY."+__name__)
|
||||
|
||||
##################################################################################################
|
||||
|
||||
|
||||
class Kodi(object):
|
||||
|
||||
|
||||
def __init__(self):
|
||||
self.artwork = artwork.Artwork(self.cursor)
|
||||
|
||||
def create_entry_path(self):
|
||||
self.cursor.execute(QU.create_path)
|
||||
|
||||
return self.cursor.fetchone()[0] + 1
|
||||
|
||||
def create_entry_file(self):
|
||||
self.cursor.execute(QU.create_file)
|
||||
|
||||
return self.cursor.fetchone()[0] + 1
|
||||
|
||||
def create_entry_person(self):
|
||||
self.cursor.execute(QU.create_person)
|
||||
|
||||
return self.cursor.fetchone()[0] + 1
|
||||
|
||||
def create_entry_genre(self):
|
||||
self.cursor.execute(QU.create_genre)
|
||||
|
||||
return self.cursor.fetchone()[0] + 1
|
||||
|
||||
def create_entry_studio(self):
|
||||
self.cursor.execute(QU.create_studio)
|
||||
|
||||
return self.cursor.fetchone()[0] + 1
|
||||
|
||||
def create_entry_bookmark(self):
|
||||
self.cursor.execute(QU.create_bookmark)
|
||||
|
||||
return self.cursor.fetchone()[0] + 1
|
||||
|
||||
def create_entry_tag(self):
|
||||
self.cursor.execute(QU.create_tag)
|
||||
|
||||
return self.cursor.fetchone()[0] + 1
|
||||
|
||||
def add_path(self, *args):
|
||||
path_id = self.get_path(*args)
|
||||
|
||||
if path_id is None:
|
||||
|
||||
path_id = self.create_entry_path()
|
||||
self.cursor.execute(QU.add_path, (path_id,) + args)
|
||||
|
||||
return path_id
|
||||
|
||||
def get_path(self, *args):
|
||||
|
||||
try:
|
||||
self.cursor.execute(QU.get_path, args)
|
||||
|
||||
return self.cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
return
|
||||
|
||||
def update_path(self, *args):
|
||||
self.cursor.execute(QU.update_path, args)
|
||||
|
||||
def remove_path(self, *args):
|
||||
self.cursor.execute(QU.delete_path, args)
|
||||
|
||||
def add_file(self, filename, path_id):
|
||||
|
||||
try:
|
||||
self.cursor.execute(QU.get_file, (filename, path_id,))
|
||||
file_id = self.cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
|
||||
file_id = self.create_entry_file()
|
||||
self.cursor.execute(QU.add_file, (file_id, path_id, filename))
|
||||
|
||||
return file_id
|
||||
|
||||
def update_file(self, *args):
|
||||
self.cursor.execute(QU.update_file, args)
|
||||
|
||||
def remove_file(self, path, *args):
|
||||
path_id = self.get_path(path)
|
||||
|
||||
if path_id is not None:
|
||||
self.cursor.execute(QU.delete_file_by_path, (path_id,) + args)
|
||||
|
||||
def get_filename(self, *args):
|
||||
|
||||
try:
|
||||
self.cursor.execute(QU.get_filename, args)
|
||||
|
||||
return self.cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
return ""
|
||||
|
||||
def add_people(self, people, *args):
|
||||
|
||||
def add_thumbnail(person_id, person, person_type):
|
||||
|
||||
if person['imageurl']:
|
||||
|
||||
art = person_type.lower()
|
||||
if "writing" in art:
|
||||
art = "writer"
|
||||
|
||||
self.artwork.update(person['imageurl'], person_id, art, "thumb")
|
||||
|
||||
def add_link(link, person_id):
|
||||
self.cursor.execute(QU.update_link.replace("{LinkType}", link), (person_id,) + args)
|
||||
|
||||
cast_order = 1
|
||||
|
||||
for person in people:
|
||||
person_id = self.get_person(person['Name'])
|
||||
|
||||
if person['Type'] == 'Actor':
|
||||
|
||||
role = person.get('Role')
|
||||
self.cursor.execute(QU.update_actor, (person_id,) + args + (role, cast_order,))
|
||||
cast_order += 1
|
||||
|
||||
elif person['Type'] == 'Director':
|
||||
add_link('director_link', person_id)
|
||||
|
||||
elif person['Type'] == 'Writer':
|
||||
add_link('writer_link', person_id)
|
||||
|
||||
elif person['Type'] == 'Artist':
|
||||
add_link('actor_link', person_id)
|
||||
|
||||
add_thumbnail(person_id, person, person['Type'])
|
||||
|
||||
def add_person(self, *args):
|
||||
|
||||
person_id = self.create_entry_person()
|
||||
self.cursor.execute(QU.add_person, (person_id,) + args)
|
||||
|
||||
return person_id
|
||||
|
||||
def get_person(self, *args):
|
||||
|
||||
try:
|
||||
self.cursor.execute(QU.get_person, args)
|
||||
|
||||
return self.cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
return self.add_person(*args)
|
||||
|
||||
def add_genres(self, genres, *args):
|
||||
|
||||
''' Delete current genres first for clean slate.
|
||||
'''
|
||||
self.cursor.execute(QU.delete_genres, args)
|
||||
|
||||
for genre in genres:
|
||||
self.cursor.execute(QU.update_genres, (self.get_genre(genre),) + args)
|
||||
|
||||
def add_genre(self, *args):
|
||||
|
||||
genre_id = self.create_entry_genre()
|
||||
self.cursor.execute(QU.add_genre, (genre_id,) + args)
|
||||
|
||||
return genre_id
|
||||
|
||||
def get_genre(self, *args):
|
||||
|
||||
try:
|
||||
self.cursor.execute(QU.get_genre, args)
|
||||
|
||||
return self.cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
return self.add_genre(*args)
|
||||
|
||||
def add_studios(self, studios, *args):
|
||||
|
||||
for studio in studios:
|
||||
|
||||
studio_id = self.get_studio(studio)
|
||||
self.cursor.execute(QU.update_studios, (studio_id,) + args)
|
||||
|
||||
def add_studio(self, *args):
|
||||
|
||||
studio_id = self.create_entry_studio()
|
||||
self.cursor.execute(QU.add_studio, (studio_id,) + args)
|
||||
|
||||
return studio_id
|
||||
|
||||
def get_studio(self, *args):
|
||||
|
||||
try:
|
||||
self.cursor.execute(QU.get_studio, args)
|
||||
|
||||
return self.cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
return self.add_studio(*args)
|
||||
|
||||
def add_streams(self, file_id, streams, runtime):
|
||||
|
||||
''' First remove any existing entries
|
||||
Then re-add video, audio and subtitles.
|
||||
'''
|
||||
self.cursor.execute(QU.delete_streams, (file_id,))
|
||||
|
||||
if streams:
|
||||
for track in streams['video']:
|
||||
|
||||
track['FileId'] = file_id
|
||||
track['Runtime'] = runtime
|
||||
self.add_stream_video(*values(track, QU.add_stream_video_obj))
|
||||
|
||||
for track in streams['audio']:
|
||||
|
||||
track['FileId'] = file_id
|
||||
self.add_stream_audio(*values(track, QU.add_stream_audio_obj))
|
||||
|
||||
for track in streams['subtitle']:
|
||||
self.add_stream_sub(*values({'language': track, 'FileId': file_id}, QU.add_stream_sub_obj))
|
||||
|
||||
def add_stream_video(self, *args):
|
||||
self.cursor.execute(QU.add_stream_video, args)
|
||||
|
||||
def add_stream_audio(self, *args):
|
||||
self.cursor.execute(QU.add_stream_audio, args)
|
||||
|
||||
def add_stream_sub(self, *args):
|
||||
self.cursor.execute(QU.add_stream_sub, args)
|
||||
|
||||
def add_playstate(self, file_id, playcount, date_played, resume, *args):
|
||||
|
||||
''' Delete the existing resume point.
|
||||
Set the watched count.
|
||||
'''
|
||||
self.cursor.execute(QU.delete_bookmark, (file_id,))
|
||||
self.set_playcount(playcount, date_played, file_id)
|
||||
|
||||
if resume:
|
||||
|
||||
bookmark_id = self.create_entry_bookmark()
|
||||
self.cursor.execute(QU.add_bookmark, (bookmark_id, file_id, resume,) + args)
|
||||
|
||||
def set_playcount(self, *args):
|
||||
self.cursor.execute(QU.update_playcount, args)
|
||||
|
||||
def add_tags(self, tags, *args):
|
||||
self.cursor.execute(QU.delete_tags, args)
|
||||
|
||||
for tag in tags:
|
||||
tag_id = self.get_tag(tag, *args)
|
||||
|
||||
def add_tag(self, *args):
|
||||
|
||||
tag_id = self.create_entry_tag()
|
||||
self.cursor.execute(QU.add_tag, (tag_id,) + args)
|
||||
|
||||
return tag_id
|
||||
|
||||
def get_tag(self, tag, *args):
|
||||
|
||||
try:
|
||||
self.cursor.execute(QU.get_tag, (tag,))
|
||||
tag_id = self.cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
tag_id = self.add_tag(tag)
|
||||
|
||||
self.cursor.execute(QU.update_tag, (tag_id,) + args)
|
||||
|
||||
return tag_id
|
||||
|
||||
def remove_tag(self, tag, *args):
|
||||
|
||||
try:
|
||||
self.cursor.execute(QU.get_tag, (tag,))
|
||||
tag_id = self.cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
return
|
||||
|
||||
self.cursor.execute(QU.delete_tag, (tag_id,) + args)
|
149
resources/lib/objects/kodi/movies.py
Normal file
149
resources/lib/objects/kodi/movies.py
Normal file
|
@ -0,0 +1,149 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
##################################################################################################
|
||||
|
||||
import logging
|
||||
|
||||
from kodi import Kodi
|
||||
import queries as QU
|
||||
|
||||
##################################################################################################
|
||||
|
||||
LOG = logging.getLogger("EMBY."+__name__)
|
||||
|
||||
##################################################################################################
|
||||
|
||||
|
||||
class Movies(Kodi):
|
||||
|
||||
|
||||
def __init__(self, cursor):
|
||||
|
||||
self.cursor = cursor
|
||||
Kodi.__init__(self)
|
||||
|
||||
def create_entry_unique_id(self):
|
||||
self.cursor.execute(QU.create_unique_id)
|
||||
|
||||
return self.cursor.fetchone()[0] + 1
|
||||
|
||||
def create_entry_rating(self):
|
||||
self.cursor.execute(QU.create_rating)
|
||||
|
||||
return self.cursor.fetchone()[0] + 1
|
||||
|
||||
def create_entry(self):
|
||||
self.cursor.execute(QU.create_movie)
|
||||
|
||||
return self.cursor.fetchone()[0] + 1
|
||||
|
||||
def create_entry_set(self):
|
||||
self.cursor.execute(QU.create_set)
|
||||
|
||||
return self.cursor.fetchone()[0] + 1
|
||||
|
||||
def create_entry_country(self):
|
||||
self.cursor.execute(QU.create_country)
|
||||
|
||||
return self.cursor.fetchone()[0] + 1
|
||||
|
||||
def get(self, *args):
|
||||
|
||||
try:
|
||||
self.cursor.execute(QU.get_movie, args)
|
||||
return self.cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
return
|
||||
|
||||
def add(self, *args):
|
||||
self.cursor.execute(QU.add_movie, args)
|
||||
|
||||
def update(self, *args):
|
||||
self.cursor.execute(QU.update_movie, args)
|
||||
|
||||
def delete(self, kodi_id, file_id):
|
||||
|
||||
self.cursor.execute(QU.delete_movie, (kodi_id,))
|
||||
self.cursor.execute(QU.delete_file, (file_id,))
|
||||
|
||||
def get_rating_id(self, *args):
|
||||
|
||||
try:
|
||||
self.cursor.execute(QU.get_rating, args)
|
||||
|
||||
return self.cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
return None
|
||||
|
||||
def add_ratings(self, *args):
|
||||
|
||||
''' Add ratings, rating type and votes.
|
||||
'''
|
||||
self.cursor.execute(QU.add_rating, args)
|
||||
|
||||
def update_ratings(self, *args):
|
||||
|
||||
''' Update rating by rating_id.
|
||||
'''
|
||||
self.cursor.execute(QU.update_rating, args)
|
||||
|
||||
def get_unique_id(self, *args):
|
||||
|
||||
try:
|
||||
self.cursor.execute(QU.get_unique_id, args)
|
||||
|
||||
return self.cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
return
|
||||
|
||||
def add_unique_id(self, *args):
|
||||
|
||||
''' Add the provider id, imdb, tvdb.
|
||||
'''
|
||||
self.cursor.execute(QU.add_unique_id, args)
|
||||
|
||||
def update_unique_id(self, *args):
|
||||
|
||||
''' Update the provider id, imdb, tvdb.
|
||||
'''
|
||||
self.cursor.execute(QU.update_unique_id, args)
|
||||
|
||||
def add_countries(self, countries, *args):
|
||||
|
||||
for country in countries:
|
||||
self.cursor.execute(QU.update_country, (self.get_country(country),) + args)
|
||||
|
||||
def add_country(self, *args):
|
||||
|
||||
country_id = self.create_entry_country()
|
||||
self.cursor.execute(QU.add_country, (country_id,) + args)
|
||||
|
||||
return country_id
|
||||
|
||||
def get_country(self, *args):
|
||||
|
||||
try:
|
||||
self.cursor.execute(QU.get_country, args)
|
||||
|
||||
return self.cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
return self.add_country(*args)
|
||||
|
||||
def add_boxset(self, *args):
|
||||
|
||||
set_id = self.create_entry_set()
|
||||
self.cursor.execute(QU.add_set, (set_id,) + args)
|
||||
|
||||
return set_id
|
||||
|
||||
def update_boxset(self, *args):
|
||||
self.cursor.execute(QU.update_set, args)
|
||||
|
||||
def set_boxset(self, *args):
|
||||
self.cursor.execute(QU.update_movie_set, args)
|
||||
|
||||
def remove_from_boxset(self, *args):
|
||||
self.cursor.execute(QU.delete_movie_set, args)
|
||||
|
||||
def delete_boxset(self, *args):
|
||||
self.cursor.execute(QU.delete_set, args)
|
231
resources/lib/objects/kodi/music.py
Normal file
231
resources/lib/objects/kodi/music.py
Normal file
|
@ -0,0 +1,231 @@
|
|||
# -*- 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, artists=None, *args):
|
||||
|
||||
try:
|
||||
if musicbrainz is not None:
|
||||
self.cursor.execute(QU.get_album, (musicbrainz,))
|
||||
album = None
|
||||
else:
|
||||
self.cursor.execute(QU.get_album_by_name, (name,))
|
||||
album = self.cursor.fetchone()
|
||||
|
||||
if album[1] and album[1].split(' / ')[0] not in artists.split(' / '):
|
||||
LOG.info("Album found, but artist doesn't match?")
|
||||
LOG.info("Album [ %s/%s ] %s", name, album[1], artists)
|
||||
|
||||
raise TypeError
|
||||
|
||||
album_id = (album or self.cursor.fetchone())[0]
|
||||
except TypeError:
|
||||
album_id = self.add_album(*(album_id, name, musicbrainz,) + args)
|
||||
|
||||
return album_id
|
||||
|
||||
def add_album(self, album_id, *args):
|
||||
|
||||
album_id = album_id or self.create_entry_album()
|
||||
self.cursor.execute(QU.add_album, (album_id,) + args)
|
||||
|
||||
return album_id
|
||||
|
||||
def update_album(self, *args):
|
||||
self.cursor.execute(QU.update_album, args)
|
||||
|
||||
def get_album_artist(self, album_id, artists):
|
||||
|
||||
try:
|
||||
self.cursor.execute(QU.get_album_artist, (album_id,))
|
||||
curr_artists = self.cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
return
|
||||
|
||||
if curr_artists != artists:
|
||||
self.update_album_artist(artists, album_id)
|
||||
|
||||
def update_album_artist(self, *args):
|
||||
self.cursor.execute(QU.update_album_artist, args)
|
||||
|
||||
def add_single(self, *args):
|
||||
self.cursor.execute(QU.add_single, args)
|
||||
|
||||
def add_song(self, *args):
|
||||
self.cursor.execute(QU.add_song, args)
|
||||
|
||||
def update_song(self, *args):
|
||||
self.cursor.execute(QU.update_song, args)
|
||||
|
||||
def link_song_artist(self, *args):
|
||||
self.cursor.execute(QU.update_song_artist, args)
|
||||
|
||||
def link_song_album(self, *args):
|
||||
self.cursor.execute(QU.update_song_album, args)
|
||||
|
||||
def rate_song(self, *args):
|
||||
self.cursor.execute(QU.update_song_rating, args)
|
||||
|
||||
def add_genres(self, kodi_id, genres, media):
|
||||
|
||||
''' Add genres, but delete current genres first.
|
||||
'''
|
||||
if media == 'album':
|
||||
self.cursor.execute(QU.delete_genres_album, (kodi_id,))
|
||||
|
||||
for genre in genres:
|
||||
|
||||
genre_id = self.get_genre(genre)
|
||||
self.cursor.execute(QU.update_genre_album, (genre_id, kodi_id))
|
||||
|
||||
elif media == 'song':
|
||||
self.cursor.execute(QU.delete_genres_song, (kodi_id,))
|
||||
|
||||
for genre in genres:
|
||||
|
||||
genre_id = self.get_genre(genre)
|
||||
self.cursor.execute(QU.update_genre_song, (genre_id, kodi_id))
|
||||
|
||||
def get_genre(self, *args):
|
||||
|
||||
try:
|
||||
self.cursor.execute(QU.get_genre, args)
|
||||
|
||||
return self.cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
return self.add_genre(*args)
|
||||
|
||||
def add_genre(self, *args):
|
||||
|
||||
genre_id = self.create_entry_genre()
|
||||
self.cursor.execute(QU.add_genre, (genre_id,) + args)
|
||||
|
||||
return genre_id
|
||||
|
||||
def delete(self, *args):
|
||||
self.cursor.execute(QU.delete_artist, args)
|
||||
|
||||
def delete_album(self, *args):
|
||||
self.cursor.execute(QU.delete_album, args)
|
||||
|
||||
def delete_song(self, *args):
|
||||
self.cursor.execute(QU.delete_song, args)
|
48
resources/lib/objects/kodi/musicvideos.py
Normal file
48
resources/lib/objects/kodi/musicvideos.py
Normal file
|
@ -0,0 +1,48 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
##################################################################################################
|
||||
|
||||
import logging
|
||||
|
||||
import queries as QU
|
||||
from kodi import Kodi
|
||||
|
||||
##################################################################################################
|
||||
|
||||
log = logging.getLogger("EMBY."+__name__)
|
||||
|
||||
##################################################################################################
|
||||
|
||||
|
||||
class MusicVideos(Kodi):
|
||||
|
||||
|
||||
def __init__(self, cursor):
|
||||
|
||||
self.cursor = cursor
|
||||
Kodi.__init__(self)
|
||||
|
||||
def create_entry(self):
|
||||
self.cursor.execute(QU.create_musicvideo)
|
||||
|
||||
return self.cursor.fetchone()[0] + 1
|
||||
|
||||
def get(self, *args):
|
||||
|
||||
try:
|
||||
self.cursor.execute(QU.get_musicvideo, args)
|
||||
|
||||
return self.cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
return
|
||||
|
||||
def add(self, *args):
|
||||
self.cursor.execute(QU.add_musicvideo, args)
|
||||
|
||||
def update(self, *args):
|
||||
self.cursor.execute(QU.update_musicvideo, args)
|
||||
|
||||
def delete(self, kodi_id, file_id):
|
||||
|
||||
self.cursor.execute(QU.delete_musicvideo, (kodi_id,))
|
||||
self.cursor.execute(QU.delete_file, (file_id,))
|
550
resources/lib/objects/kodi/queries.py
Normal file
550
resources/lib/objects/kodi/queries.py
Normal file
|
@ -0,0 +1,550 @@
|
|||
|
||||
''' Queries for the Kodi database. obj reflect key/value to retrieve from emby items.
|
||||
Some functions require additional information, therefore obj do not always reflect
|
||||
the Kodi database query values.
|
||||
'''
|
||||
create_path = """ SELECT coalesce(max(idPath), 0)
|
||||
FROM path
|
||||
"""
|
||||
create_file = """ SELECT coalesce(max(idFile), 0)
|
||||
FROM files
|
||||
"""
|
||||
create_person = """ SELECT coalesce(max(actor_id), 0)
|
||||
FROM actor
|
||||
"""
|
||||
create_genre = """ SELECT coalesce(max(genre_id), 0)
|
||||
FROM genre
|
||||
"""
|
||||
create_studio = """ SELECT coalesce(max(studio_id), 0)
|
||||
FROM studio
|
||||
"""
|
||||
create_bookmark = """ SELECT coalesce(max(idBookmark), 0)
|
||||
FROM bookmark
|
||||
"""
|
||||
create_tag = """ SELECT coalesce(max(tag_id), 0)
|
||||
FROM tag
|
||||
"""
|
||||
create_unique_id = """ SELECT coalesce(max(uniqueid_id), 0)
|
||||
FROM uniqueid
|
||||
"""
|
||||
create_rating = """ SELECT coalesce(max(rating_id), 0)
|
||||
FROM rating
|
||||
"""
|
||||
create_movie = """ SELECT coalesce(max(idMovie), 0)
|
||||
FROM movie
|
||||
"""
|
||||
create_set = """ SELECT coalesce(max(idSet), 0)
|
||||
FROM sets
|
||||
"""
|
||||
create_country = """ SELECT coalesce(max(country_id), 0)
|
||||
FROM country
|
||||
"""
|
||||
create_musicvideo = """ SELECT coalesce(max(idMVideo), 0)
|
||||
FROM musicvideo
|
||||
"""
|
||||
create_tvshow = """ SELECT coalesce(max(idShow), 0)
|
||||
FROM tvshow
|
||||
"""
|
||||
create_season = """ SELECT coalesce(max(idSeason), 0)
|
||||
FROM seasons
|
||||
"""
|
||||
create_episode = """ SELECT coalesce(max(idEpisode), 0)
|
||||
FROM episode
|
||||
"""
|
||||
|
||||
|
||||
get_path = """ SELECT idPath
|
||||
FROM path
|
||||
WHERE strPath = ?
|
||||
"""
|
||||
get_path_obj = [ "{Path}"
|
||||
]
|
||||
get_file = """ SELECT idFile
|
||||
FROM files
|
||||
WHERE idPath = ?
|
||||
AND strFilename = ?
|
||||
"""
|
||||
get_file_obj = [ "{FileId}"
|
||||
]
|
||||
get_filename = """ SELECT strFilename
|
||||
FROM files
|
||||
WHERE idFile = ?
|
||||
"""
|
||||
get_person = """ SELECT actor_id
|
||||
FROM actor
|
||||
WHERE name = ?
|
||||
COLLATE NOCASE
|
||||
"""
|
||||
get_genre = """ SELECT genre_id
|
||||
FROM genre
|
||||
WHERE name = ?
|
||||
COLLATE NOCASE
|
||||
"""
|
||||
get_studio = """ SELECT studio_id
|
||||
FROM studio
|
||||
WHERE name = ?
|
||||
COLLATE NOCASE
|
||||
"""
|
||||
get_tag = """ SELECT tag_id
|
||||
FROM tag
|
||||
WHERE name = ?
|
||||
COLLATE NOCASE
|
||||
"""
|
||||
get_tag_movie_obj = [ "Favorite movies","{MovieId}","movie"
|
||||
]
|
||||
get_tag_mvideo_obj = [ "Favorite musicvideos","{MvideoId}","musicvideo"
|
||||
]
|
||||
get_tag_episode_obj = [ "Favorite tvshows","{KodiId}","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, userrating, 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}","{CriticRating}","{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, c02, c04, c05, c08, c09, c12, c13, c14, c15)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
"""
|
||||
add_tvshow_obj = [ "{ShowId}","{Title}","{Plot}","{Status}","{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 = ?, userrating = ?, premiered = ?
|
||||
WHERE idMovie = ?
|
||||
"""
|
||||
update_movie_obj = [ "{Title}","{Plot}","{ShortPlot}","{Tagline}","{Votes}","{RatingId}",
|
||||
"{Writers}","{Year}","{Unique}","{SortTitle}","{Runtime}",
|
||||
"{Mpaa}","{Genre}","{Directors}","{Title}","{Studio}","{Trailer}",
|
||||
"{Country}","{CriticRating}","{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 = ?, c02 = ?, c04 = ?, c05 = ?, c08 = ?, c09 = ?,
|
||||
c12 = ?, c13 = ?, c14 = ?, c15 = ?
|
||||
WHERE idShow = ?
|
||||
"""
|
||||
update_tvshow_obj = [ "{Title}","{Plot}","{Status}","{RatingId}","{Premiere}","{Genre}","{Title}",
|
||||
"{Unique}","{Mpaa}","{Studio}","{SortTitle}","{ShowId}"
|
||||
]
|
||||
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_id = ?
|
||||
AND media_type = ?
|
||||
"""
|
||||
delete_tag_movie_obj = [ "Favorite movies","{MovieId}","movie"
|
||||
]
|
||||
delete_tag_mvideo_obj = [ "Favorite musicvideos","{MvideoId}","musicvideo"
|
||||
]
|
||||
delete_tag_episode_obj = [ "Favorite tvshows","{KodiId}","tvshow"
|
||||
]
|
||||
delete_movie = """ DELETE FROM movie
|
||||
WHERE idMovie = ?
|
||||
"""
|
||||
delete_movie_obj = [ "{KodiId}","{FileId}"
|
||||
]
|
||||
delete_set = """ DELETE FROM sets
|
||||
WHERE idSet = ?
|
||||
"""
|
||||
delete_set_obj = [ "{KodiId}"
|
||||
]
|
||||
delete_movie_set = """ UPDATE movie
|
||||
SET idSet = null
|
||||
WHERE idMovie = ?
|
||||
"""
|
||||
delete_movie_set_obj = [ "{MovieId}"
|
||||
]
|
||||
delete_musicvideo = """ DELETE FROM musicvideo
|
||||
WHERE idMVideo = ?
|
||||
"""
|
||||
delete_musicvideo_obj = [ "{MvideoId}", "{FileId}"
|
||||
]
|
||||
delete_tvshow = """ DELETE FROM tvshow
|
||||
WHERE idShow = ?
|
||||
"""
|
||||
delete_season = """ DELETE FROM seasons
|
||||
WHERE idSeason = ?
|
||||
"""
|
||||
delete_episode = """ DELETE FROM episode
|
||||
WHERE idEpisode = ?
|
||||
"""
|
||||
delete_backdrops = """ DELETE FROM art
|
||||
WHERE media_id = ?
|
||||
AND media_type = ?
|
||||
AND type LIKE ?
|
||||
"""
|
197
resources/lib/objects/kodi/queries_music.py
Normal file
197
resources/lib/objects/kodi/queries_music.py
Normal file
|
@ -0,0 +1,197 @@
|
|||
|
||||
create_artist = """ SELECT coalesce(max(idArtist), 1)
|
||||
FROM artist
|
||||
"""
|
||||
create_album = """ SELECT coalesce(max(idAlbum), 0)
|
||||
FROM album
|
||||
"""
|
||||
create_song = """ SELECT coalesce(max(idSong), 0)
|
||||
FROM song
|
||||
"""
|
||||
create_genre = """ SELECT coalesce(max(idGenre), 0)
|
||||
FROM genre
|
||||
"""
|
||||
|
||||
|
||||
|
||||
get_artist = """ SELECT idArtist, strArtist
|
||||
FROM artist
|
||||
WHERE strMusicBrainzArtistID = ?
|
||||
"""
|
||||
get_artist_obj = [ "{ArtistId}","{Name}","{UniqueId}"
|
||||
]
|
||||
get_artist_by_name = """ SELECT idArtist
|
||||
FROM artist
|
||||
WHERE strArtist = ?
|
||||
COLLATE NOCASE
|
||||
"""
|
||||
get_artist_by_id = """ SELECT *
|
||||
FROM artist
|
||||
WHERE idArtist = ?
|
||||
"""
|
||||
get_artist_by_id_obj = [ "{ArtistId}"
|
||||
]
|
||||
get_album_by_id = """ SELECT *
|
||||
FROM album
|
||||
WHERE idAlbum = ?
|
||||
"""
|
||||
get_album_by_id_obj = [ "{AlbumId}"
|
||||
]
|
||||
get_song_by_id = """ SELECT *
|
||||
FROM song
|
||||
WHERE idSong = ?
|
||||
"""
|
||||
get_song_by_id_obj = [ "{SongId}"
|
||||
]
|
||||
get_album = """ SELECT idAlbum
|
||||
FROM album
|
||||
WHERE strMusicBrainzAlbumID = ?
|
||||
"""
|
||||
get_album_obj = [ "{AlbumId}","{Title}","{UniqueId}","{Artists}","album"
|
||||
]
|
||||
get_album_by_name = """ SELECT idAlbum, strArtists
|
||||
FROM album
|
||||
WHERE strAlbum = ?
|
||||
"""
|
||||
get_album_artist = """ SELECT strArtists
|
||||
FROM album
|
||||
WHERE idAlbum = ?
|
||||
"""
|
||||
get_album_artist_obj = [ "{AlbumId}","{strAlbumArtists}"
|
||||
]
|
||||
get_genre = """ SELECT idGenre
|
||||
FROM genre
|
||||
WHERE strGenre = ?
|
||||
COLLATE NOCASE
|
||||
"""
|
||||
get_total_episodes = """ SELECT totalCount
|
||||
FROM tvshowcounts
|
||||
WHERE idShow = ?
|
||||
"""
|
||||
|
||||
|
||||
|
||||
add_artist = """ INSERT INTO artist(idArtist, strArtist, strMusicBrainzArtistID)
|
||||
VALUES (?, ?, ?)
|
||||
"""
|
||||
add_album = """ INSERT INTO album(idAlbum, strAlbum, strMusicBrainzAlbumID, strReleaseType)
|
||||
VALUES (?, ?, ?, ?)
|
||||
"""
|
||||
add_single = """ INSERT INTO album(idAlbum, strGenres, iYear, strReleaseType)
|
||||
VALUES (?, ?, ?, ?)
|
||||
"""
|
||||
add_single_obj = [ "{AlbumId}","{Genre}","{Year}","single"
|
||||
]
|
||||
add_song = """ INSERT INTO song(idSong, idAlbum, idPath, strArtists, strGenres, strTitle, iTrack,
|
||||
iDuration, iYear, strFileName, strMusicBrainzTrackID, iTimesPlayed, lastplayed,
|
||||
rating, comment, dateAdded)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
"""
|
||||
add_song_obj = [ "{SongId}","{AlbumId}","{PathId}","{Artists}","{Genre}","{Title}","{Index}",
|
||||
"{Runtime}","{Year}","{Filename}","{UniqueId}","{PlayCount}","{DatePlayed}","{Rating}",
|
||||
"{Comment}","{DateAdded}"
|
||||
]
|
||||
add_genre = """ INSERT INTO genre(idGenre, strGenre)
|
||||
VALUES (?, ?)
|
||||
"""
|
||||
add_genres_obj = [ "{AlbumId}","{Genres}","album"
|
||||
]
|
||||
|
||||
|
||||
|
||||
update_path = """ UPDATE path
|
||||
SET strPath = ?
|
||||
WHERE idPath = ?
|
||||
"""
|
||||
update_path_obj = [ "{Path}","{PathId}"
|
||||
]
|
||||
update_role = """ INSERT OR REPLACE INTO role(idRole, strRole)
|
||||
VALUES (?, ?)
|
||||
"""
|
||||
update_role_obj = [ 1,"Composer"
|
||||
]
|
||||
update_artist_name = """ UPDATE artist
|
||||
SET strArtist = ?
|
||||
WHERE idArtist = ?
|
||||
"""
|
||||
update_artist_name_obj = [ "{Name}","{ArtistId}"
|
||||
]
|
||||
update_artist = """ UPDATE artist
|
||||
SET strGenres = ?, strBiography = ?, strImage = ?, strFanart = ?, lastScraped = ?
|
||||
WHERE idArtist = ?
|
||||
"""
|
||||
update_link = """ INSERT OR REPLACE INTO album_artist(idArtist, idAlbum, strArtist)
|
||||
VALUES (?, ?, ?)
|
||||
"""
|
||||
update_link_obj = [ "{ArtistId}","{AlbumId}","{Name}"
|
||||
]
|
||||
update_discography = """ INSERT OR REPLACE INTO discography(idArtist, strAlbum, strYear)
|
||||
VALUES (?, ?, ?)
|
||||
"""
|
||||
update_discography_obj = [ "{ArtistId}","{Title}","{Year}"
|
||||
]
|
||||
update_album = """ UPDATE album
|
||||
SET strArtists = ?, iYear = ?, strGenres = ?, strReview = ?, strImage = ?,
|
||||
iUserrating = ?, lastScraped = ?, strReleaseType = ?
|
||||
WHERE idAlbum = ?
|
||||
"""
|
||||
update_album_obj = [ "{Artists}","{Year}","{Genre}","{Bio}","{Thumb}","{Rating}","{LastScraped}",
|
||||
"album","{AlbumId}"
|
||||
]
|
||||
update_album_artist = """ UPDATE album
|
||||
SET strArtists = ?
|
||||
WHERE idAlbum = ?
|
||||
"""
|
||||
update_song = """ UPDATE song
|
||||
SET idAlbum = ?, strArtists = ?, strGenres = ?, strTitle = ?, iTrack = ?,
|
||||
iDuration = ?, iYear = ?, strFilename = ?, iTimesPlayed = ?, lastplayed = ?,
|
||||
rating = ?, comment = ?, dateAdded = ?
|
||||
WHERE idSong = ?
|
||||
"""
|
||||
update_song_obj = [ "{AlbumId}","{Artists}","{Genre}","{Title}","{Index}","{Runtime}","{Year}",
|
||||
"{Filename}","{PlayCount}","{DatePlayed}","{Rating}","{Comment}",
|
||||
"{DateAdded}","{SongId}"
|
||||
]
|
||||
update_song_artist = """ INSERT OR REPLACE INTO song_artist(idArtist, idSong, idRole, iOrder, strArtist)
|
||||
VALUES (?, ?, ?, ?, ?)
|
||||
"""
|
||||
update_song_artist_obj = [ "{ArtistId}","{SongId}",1,"{Index}","{Name}"
|
||||
]
|
||||
update_song_album = """ INSERT OR REPLACE INTO albuminfosong(idAlbumInfoSong, idAlbumInfo, iTrack,
|
||||
strTitle, iDuration)
|
||||
VALUES (?, ?, ?, ?, ?)
|
||||
"""
|
||||
update_song_album_obj = [ "{SongId}","{AlbumId}","{Index}","{Title}","{Runtime}"
|
||||
]
|
||||
update_song_rating = """ UPDATE song
|
||||
SET iTimesPlayed = ?, lastplayed = ?, rating = ?
|
||||
WHERE idSong = ?
|
||||
"""
|
||||
update_song_rating_obj = [ "{PlayCount}","{DatePlayed}","{Rating}","{KodiId}"
|
||||
]
|
||||
update_genre_album = """ INSERT OR REPLACE INTO album_genre(idGenre, idAlbum)
|
||||
VALUES (?, ?)
|
||||
"""
|
||||
update_genre_song = """ INSERT OR REPLACE INTO song_genre(idGenre, idSong)
|
||||
VALUES (?, ?)
|
||||
"""
|
||||
update_genre_song_obj = [ "{SongId}","{Genres}","song"
|
||||
]
|
||||
|
||||
|
||||
|
||||
delete_genres_album = """ DELETE FROM album_genre
|
||||
WHERE idAlbum = ?
|
||||
"""
|
||||
delete_genres_song = """ DELETE FROM song_genre
|
||||
WHERE idSong = ?
|
||||
"""
|
||||
delete_artist = """ DELETE FROM artist
|
||||
WHERE idArtist = ?
|
||||
"""
|
||||
delete_album = """ DELETE FROM album
|
||||
WHERE idAlbum = ?
|
||||
"""
|
||||
delete_song = """ DELETE FROM song
|
||||
WHERE idSong = ?
|
||||
"""
|
11
resources/lib/objects/kodi/queries_texture.py
Normal file
11
resources/lib/objects/kodi/queries_texture.py
Normal file
|
@ -0,0 +1,11 @@
|
|||
|
||||
get_cache = """ SELECT cachedurl
|
||||
FROM texture
|
||||
WHERE url = ?
|
||||
"""
|
||||
|
||||
|
||||
|
||||
delete_cache = """ DELETE FROM texture
|
||||
WHERE url = ?
|
||||
"""
|
156
resources/lib/objects/kodi/tvshows.py
Normal file
156
resources/lib/objects/kodi/tvshows.py
Normal file
|
@ -0,0 +1,156 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
##################################################################################################
|
||||
|
||||
import logging
|
||||
|
||||
import queries as QU
|
||||
from kodi import Kodi
|
||||
|
||||
##################################################################################################
|
||||
|
||||
LOG = logging.getLogger("EMBY."+__name__)
|
||||
|
||||
##################################################################################################
|
||||
|
||||
|
||||
class TVShows(Kodi):
|
||||
|
||||
|
||||
def __init__(self, cursor):
|
||||
|
||||
self.cursor = cursor
|
||||
Kodi.__init__(self)
|
||||
|
||||
def create_entry_unique_id(self):
|
||||
self.cursor.execute(QU.create_unique_id)
|
||||
|
||||
return self.cursor.fetchone()[0] + 1
|
||||
|
||||
def create_entry_rating(self):
|
||||
self.cursor.execute(QU.create_rating)
|
||||
|
||||
return self.cursor.fetchone()[0] + 1
|
||||
|
||||
def create_entry(self):
|
||||
self.cursor.execute(QU.create_tvshow)
|
||||
|
||||
return self.cursor.fetchone()[0] + 1
|
||||
|
||||
def create_entry_season(self):
|
||||
self.cursor.execute(QU.create_season)
|
||||
|
||||
return self.cursor.fetchone()[0] + 1
|
||||
|
||||
def create_entry_episode(self):
|
||||
self.cursor.execute(QU.create_episode)
|
||||
|
||||
return self.cursor.fetchone()[0] + 1
|
||||
|
||||
def get(self, *args):
|
||||
|
||||
try:
|
||||
self.cursor.execute(QU.get_tvshow, args)
|
||||
|
||||
return self.cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
return
|
||||
|
||||
def get_episode(self, *args):
|
||||
|
||||
try:
|
||||
self.cursor.execute(QU.get_episode, args)
|
||||
|
||||
return self.cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
return
|
||||
|
||||
def get_rating_id(self, *args):
|
||||
|
||||
try:
|
||||
self.cursor.execute(QU.get_rating, args)
|
||||
|
||||
return self.cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
return
|
||||
|
||||
def add_ratings(self, *args):
|
||||
self.cursor.execute(QU.add_rating, args)
|
||||
|
||||
def update_ratings(self, *args):
|
||||
self.cursor.execute(QU.update_rating, args)
|
||||
|
||||
def get_total_episodes(self, *args):
|
||||
|
||||
try:
|
||||
self.cursor.execute(QU.get_total_episodes, args)
|
||||
|
||||
return self.cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
return
|
||||
|
||||
def get_unique_id(self, *args):
|
||||
|
||||
try:
|
||||
self.cursor.execute(QU.get_unique_id, args)
|
||||
|
||||
return self.cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
return
|
||||
|
||||
def add_unique_id(self, *args):
|
||||
self.cursor.execute(QU.add_unique_id, args)
|
||||
|
||||
def update_unique_id(self, *args):
|
||||
self.cursor.execute(QU.update_unique_id, args)
|
||||
|
||||
def add(self, *args):
|
||||
self.cursor.execute(QU.add_tvshow, args)
|
||||
|
||||
def update(self, *args):
|
||||
self.cursor.execute(QU.update_tvshow, args)
|
||||
|
||||
def link(self, *args):
|
||||
self.cursor.execute(QU.update_tvshow_link, args)
|
||||
|
||||
def get_season(self, name, *args):
|
||||
|
||||
self.cursor.execute(QU.get_season, args)
|
||||
try:
|
||||
season_id = self.cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
season_id = self.add_season(*args)
|
||||
|
||||
if name:
|
||||
self.cursor.execute(QU.update_season, (name, season_id))
|
||||
|
||||
return season_id
|
||||
|
||||
def add_season(self, *args):
|
||||
|
||||
season_id = self.create_entry_season()
|
||||
self.cursor.execute(QU.add_season, (season_id,) + args)
|
||||
|
||||
return season_id
|
||||
|
||||
def get_by_unique_id(self, *args):
|
||||
self.cursor.execute(QU.get_show_by_unique_id, args)
|
||||
|
||||
return self.cursor.fetchall()
|
||||
|
||||
def add_episode(self, *args):
|
||||
self.cursor.execute(QU.add_episode, args)
|
||||
|
||||
def update_episode(self, *args):
|
||||
self.cursor.execute(QU.update_episode, args)
|
||||
|
||||
def delete_tvshow(self, *args):
|
||||
self.cursor.execute(QU.delete_tvshow, args)
|
||||
|
||||
def delete_season(self, *args):
|
||||
self.cursor.execute(QU.delete_season, args)
|
||||
|
||||
def delete_episode(self, kodi_id, file_id):
|
||||
|
||||
self.cursor.execute(QU.delete_episode, (kodi_id,))
|
||||
self.cursor.execute(QU.delete_file, (file_id,))
|
|
@ -2,475 +2,351 @@
|
|||
|
||||
##################################################################################################
|
||||
|
||||
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, settings, Local
|
||||
|
||||
##################################################################################################
|
||||
|
||||
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.item_ids = []
|
||||
|
||||
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 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'])
|
||||
|
||||
if not settings('syncRottenTomatoes.bool'):
|
||||
obj['CriticRating'] = None
|
||||
|
||||
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['Mpaa'] = API.get_mpaa(obj['Mpaa'])
|
||||
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'] = Local(obj['DateAdded']).split('.')[0].replace('T', " ")
|
||||
obj['DatePlayed'] = None if not obj['DatePlayed'] else Local(obj['DatePlayed']).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")
|
||||
self.item_ids.append(obj['Id'])
|
||||
|
||||
return not update
|
||||
|
||||
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 new ratings Kodi 17
|
||||
if self.kodi_version >= 17:
|
||||
ratingid = self.kodi_db.get_ratingid(movieid)
|
||||
Process movies inside boxset.
|
||||
Process removals from boxset.
|
||||
'''
|
||||
API = api.API(item, self.server['auth/server-address'])
|
||||
obj = self.objects.map(item, 'Boxset')
|
||||
|
||||
self.kodi_db.update_ratings(movieid, "movie", "default", rating, votecount,ratingid)
|
||||
obj['Overview'] = API.get_overview(obj['Overview'])
|
||||
|
||||
# update new uniqueid Kodi 17
|
||||
if self.kodi_version >= 17:
|
||||
uniqueid = self.kodi_db.get_uniqueid(movieid)
|
||||
|
||||
self.kodi_db.update_uniqueid(movieid, "movie", imdb, "imdb",uniqueid)
|
||||
|
||||
# Update the movie entry
|
||||
if self.kodi_version >= 17:
|
||||
self.kodi_db.update_movie_17(title, plot, shortplot, tagline, votecount, uniqueid,
|
||||
writer, year, uniqueid, sorttitle, runtime, mpaa, genre,
|
||||
director, title, studio, trailer, country, year,
|
||||
movieid)
|
||||
else:
|
||||
self.kodi_db.update_movie(title, plot, shortplot, tagline, votecount, rating,
|
||||
writer, year, imdb, sorttitle, runtime, mpaa, genre,
|
||||
director, title, studio, trailer, country, movieid)
|
||||
|
||||
# Update the checksum in emby table
|
||||
emby_db.updateReference(itemid, checksum)
|
||||
|
||||
##### OR ADD THE MOVIE #####
|
||||
else:
|
||||
log.info("ADD movie itemid: %s - Title: %s", itemid, title)
|
||||
|
||||
# add new ratings Kodi 17
|
||||
if self.kodi_version >= 17:
|
||||
ratingid = self.kodi_db.create_entry_rating()
|
||||
|
||||
self.kodi_db.add_ratings(ratingid, movieid, "movie", "default", rating, votecount)
|
||||
|
||||
# add new uniqueid Kodi 17
|
||||
if self.kodi_version >= 17:
|
||||
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
|
||||
if self.kodi_version >= 17:
|
||||
self.kodi_db.add_movie_17(movieid, fileid, title, plot, shortplot, tagline,
|
||||
votecount, uniqueid, writer, year, uniqueid, sorttitle,
|
||||
runtime, mpaa, genre, director, title, studio, trailer,
|
||||
country, year)
|
||||
else:
|
||||
self.kodi_db.add_movie(movieid, fileid, title, plot, shortplot, tagline,
|
||||
votecount, rating, writer, year, imdb, sorttitle,
|
||||
runtime, mpaa, genre, director, title, studio, trailer,
|
||||
country)
|
||||
|
||||
# Create the reference in emby table
|
||||
emby_db.addReference(itemid, movieid, "Movie", "movie", fileid, pathid, None,
|
||||
checksum, viewid)
|
||||
|
||||
# Update the path
|
||||
self.kodi_db.update_path(pathid, path, "movies", "metadata.local")
|
||||
# Update the file
|
||||
self.kodi_db.update_file(fileid, filename, pathid, dateadded)
|
||||
|
||||
# Process countries
|
||||
if 'ProductionLocations' in item:
|
||||
self.kodi_db.add_countries(movieid, item['ProductionLocations'])
|
||||
# Process cast
|
||||
people = artwork.get_people_artwork(item['People'])
|
||||
self.kodi_db.add_people(movieid, people, "movie")
|
||||
# Process genres
|
||||
self.kodi_db.add_genres(movieid, genres, "movie")
|
||||
# Process artwork
|
||||
artwork.add_artwork(artwork.get_all_artwork(item), movieid, "movie", self.kodicursor)
|
||||
# Process stream details
|
||||
streams = API.get_media_streams()
|
||||
self.kodi_db.add_streams(fileid, streams, runtime)
|
||||
# Process studios
|
||||
self.kodi_db.add_studios(movieid, studios, "movie")
|
||||
# Process tags: view, emby tags
|
||||
tags = [viewtag]
|
||||
tags.extend(item['Tags'])
|
||||
if userdata['Favorite']:
|
||||
tags.append("Favorite movies")
|
||||
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.
|
||||
'''
|
||||
try:
|
||||
# Try to convert tuple to dictionary
|
||||
current = dict(current_movies)
|
||||
current = self.emby_db.get_item_id_by_parent_id(*values(obj, QUEM.get_item_id_by_parent_boxset_obj))
|
||||
movies = dict(current)
|
||||
except ValueError:
|
||||
current = {}
|
||||
movies = {}
|
||||
|
||||
# Sort current titles
|
||||
for current_movie in current:
|
||||
process.append(current_movie)
|
||||
obj['Current'] = movies
|
||||
|
||||
# New list to compare
|
||||
for movie in emby.getMovies_byBoxset(boxsetid)['Items']:
|
||||
for all_movies in server.get_movies_by_boxset(obj['Id']):
|
||||
for movie in all_movies['Items']:
|
||||
|
||||
itemid = movie['Id']
|
||||
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 obj['Current']:
|
||||
|
||||
# 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'].pop(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'] = Local(obj['DatePlayed']).split('.')[0].replace('T', " ")
|
||||
|
||||
if obj['Favorite']:
|
||||
self.get_tag(*values(obj, QU.get_tag_movie_obj))
|
||||
else:
|
||||
self.kodi_db.remove_tag(movieid, "Favorite movies", "movie")
|
||||
self.remove_tag(*values(obj, QU.delete_tag_movie_obj))
|
||||
|
||||
# Process playstates
|
||||
playcount = userdata['PlayCount']
|
||||
dateplayed = userdata['LastPlayedDate']
|
||||
resume = API.adjust_resume(userdata['Resume'])
|
||||
total = round(float(runtime), 6)
|
||||
LOG.debug("New resume point %s: %s", obj['Id'], obj['Resume'])
|
||||
self.add_playstate(*values(obj, QU.add_bookmark_obj))
|
||||
self.emby_db.update_reference(*values(obj, QUEM.update_reference_obj))
|
||||
LOG.info("USERDATA movie [%s/%s] %s: %s", obj['FileId'], obj['MovieId'], obj['Id'], obj['Title'])
|
||||
|
||||
log.debug("%s New resume point: %s", itemid, resume)
|
||||
@stop()
|
||||
@emby_item()
|
||||
def remove(self, item_id, e_item):
|
||||
|
||||
self.kodi_db.add_playstate(fileid, resume, total, playcount, dateplayed)
|
||||
emby_db.updateReference(itemid, checksum)
|
||||
''' Remove movieid, fileid, emby reference.
|
||||
Remove artwork, boxset
|
||||
'''
|
||||
obj = {'Id': item_id}
|
||||
|
||||
def remove(self, itemid):
|
||||
# Remove movieid, fileid, emby reference
|
||||
emby_db = self.emby_db
|
||||
artwork = self.artwork
|
||||
|
||||
emby_dbitem = emby_db.getItem_byId(itemid)
|
||||
try:
|
||||
kodiid = emby_dbitem[0]
|
||||
fileid = emby_dbitem[1]
|
||||
mediatype = emby_dbitem[4]
|
||||
log.info("Removing %sid: %s fileid: %s", mediatype, kodiid, fileid)
|
||||
obj['KodiId'] = e_item[0]
|
||||
obj['FileId'] = e_item[1]
|
||||
obj['Media'] = e_item[4]
|
||||
except TypeError:
|
||||
return
|
||||
|
||||
# Remove the emby reference
|
||||
emby_db.removeItem(itemid)
|
||||
# Remove artwork
|
||||
artwork.delete_artwork(kodiid, mediatype, self.kodicursor)
|
||||
self.artwork.delete(obj['KodiId'], obj['Media'])
|
||||
|
||||
if mediatype == "movie":
|
||||
self.kodi_db.remove_movie(kodiid, fileid)
|
||||
if obj['Media'] == 'movie':
|
||||
self.delete(*values(obj, QU.delete_movie_obj))
|
||||
elif obj['Media'] == 'set':
|
||||
|
||||
elif mediatype == "set":
|
||||
# Delete kodi boxset
|
||||
boxset_movies = emby_db.getItem_byParentId(kodiid, "movie")
|
||||
for movie in boxset_movies:
|
||||
embyid = movie[0]
|
||||
movieid = movie[1]
|
||||
self.kodi_db.remove_from_boxset(movieid)
|
||||
# Update emby reference
|
||||
emby_db.updateParentId(embyid, None)
|
||||
for movie in self.emby_db.get_item_by_parent_id(*values(obj, QUEM.get_item_by_parent_movie_obj)):
|
||||
|
||||
temp_obj = dict(obj)
|
||||
temp_obj['MovieId'] = movie[1]
|
||||
temp_obj['Movie'] = movie[0]
|
||||
self.remove_from_boxset(*values(temp_obj, QU.delete_movie_set_obj))
|
||||
self.emby_db.update_parent_id(*values(temp_obj, QUEM.delete_parent_boxset_obj))
|
||||
|
||||
self.kodi_db.remove_boxset(kodiid)
|
||||
self.delete_boxset(*values(obj, QU.delete_set_obj))
|
||||
|
||||
log.info("Deleted %s %s from kodi database", mediatype, itemid)
|
||||
self.emby_db.remove_item(*values(obj, QUEM.delete_item_obj))
|
||||
LOG.info("DELETE %s [%s/%s] %s", obj['Media'], obj['FileId'], obj['KodiId'], obj['Id'])
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -2,298 +2,240 @@
|
|||
|
||||
##################################################################################################
|
||||
|
||||
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, Local
|
||||
|
||||
##################################################################################################
|
||||
|
||||
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.item_ids = []
|
||||
|
||||
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'] = Local(obj['DateAdded']).split('.')[0].replace('T', " ")
|
||||
obj['DatePlayed'] = None if not obj['DatePlayed'] else Local(obj['DatePlayed']).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'] = Local(obj['Premiere']) if obj['Premiere'] else 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']
|
||||
|
||||
# fileId information
|
||||
checksum = API.get_checksum()
|
||||
dateadded = API.get_date_created()
|
||||
userdata = API.get_userdata()
|
||||
playcount = userdata['PlayCount']
|
||||
dateplayed = userdata['LastPlayedDate']
|
||||
|
||||
# item details
|
||||
runtime = API.get_runtime()
|
||||
plot = API.get_overview()
|
||||
title = item['Name']
|
||||
year = item.get('ProductionYear')
|
||||
genres = item['Genres']
|
||||
genre = " / ".join(genres)
|
||||
studios = API.get_studios()
|
||||
studio = " / ".join(studios)
|
||||
artist = " / ".join(item.get('Artists'))
|
||||
album = item.get('Album')
|
||||
track = item.get('Track')
|
||||
people = API.get_people()
|
||||
director = " / ".join(people['Director'])
|
||||
self.musicvideo_add(obj)
|
||||
|
||||
|
||||
##### GET THE FILE AND PATH #####
|
||||
playurl = API.get_file_path()
|
||||
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")
|
||||
self.item_ids.append(obj['Id'])
|
||||
|
||||
if "\\" in playurl:
|
||||
# Local path
|
||||
filename = playurl.rsplit("\\", 1)[1]
|
||||
else: # Network share
|
||||
filename = playurl.rsplit("/", 1)[1]
|
||||
return not update
|
||||
|
||||
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))
|
||||
|
||||
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
|
||||
self.kodi_db.update_musicvideo(title, runtime, director, studio, year, plot, album,
|
||||
artist, genre, track, mvideoid)
|
||||
|
||||
# Update the checksum in emby table
|
||||
emby_db.updateReference(itemid, checksum)
|
||||
|
||||
##### OR ADD THE MUSIC VIDEO #####
|
||||
else:
|
||||
log.info("ADD mvideo itemid: %s - Title: %s", itemid, title)
|
||||
|
||||
# Add path
|
||||
pathid = self.kodi_db.add_path(path)
|
||||
# Add the file
|
||||
fileid = self.kodi_db.add_file(filename, pathid)
|
||||
|
||||
# Create the musicvideo entry
|
||||
self.kodi_db.add_musicvideo(mvideoid, fileid, title, runtime, director, studio,
|
||||
year, plot, album, artist, genre, track)
|
||||
|
||||
# Create the reference in emby table
|
||||
emby_db.addReference(itemid, mvideoid, "MusicVideo", "musicvideo", fileid, pathid,
|
||||
checksum=checksum, mediafolderid=viewid)
|
||||
|
||||
# Update the path
|
||||
self.kodi_db.update_path(pathid, path, "musicvideos", "metadata.local")
|
||||
# Update the file
|
||||
self.kodi_db.update_file(fileid, filename, pathid, dateadded)
|
||||
|
||||
# Process cast
|
||||
people = item['People']
|
||||
artists = item['ArtistItems']
|
||||
for artist in artists:
|
||||
artist['Type'] = "Artist"
|
||||
people.extend(artists)
|
||||
people = artwork.get_people_artwork(people)
|
||||
self.kodi_db.add_people(mvideoid, people, "musicvideo")
|
||||
# Process genres
|
||||
self.kodi_db.add_genres(mvideoid, genres, "musicvideo")
|
||||
# Process artwork
|
||||
artwork.add_artwork(artwork.get_all_artwork(item), mvideoid, "musicvideo", kodicursor)
|
||||
# Process stream details
|
||||
streams = API.get_media_streams()
|
||||
self.kodi_db.add_streams(fileid, streams, runtime)
|
||||
# Process studios
|
||||
self.kodi_db.add_studios(mvideoid, studios, "musicvideo")
|
||||
# Process tags: view, emby tags
|
||||
tags = [viewtag]
|
||||
tags.extend(item['Tags'])
|
||||
if userdata['Favorite']:
|
||||
tags.append("Favorite musicvideos")
|
||||
self.kodi_db.add_tags(mvideoid, tags, "musicvideo")
|
||||
# Process playstates
|
||||
resume = API.adjust_resume(userdata['Resume'])
|
||||
total = round(float(runtime), 6)
|
||||
self.kodi_db.add_playstate(fileid, resume, total, playcount, dateplayed)
|
||||
|
||||
return True
|
||||
|
||||
def updateUserdata(self, item):
|
||||
# This updates: Favorite, LastPlayedDate, Playcount, PlaybackPositionTicks
|
||||
# Poster with progress bar
|
||||
emby_db = self.emby_db
|
||||
API = api.API(item)
|
||||
|
||||
# Get emby information
|
||||
itemid = item['Id']
|
||||
checksum = API.get_checksum()
|
||||
userdata = API.get_userdata()
|
||||
runtime = API.get_runtime()
|
||||
|
||||
# Get Kodi information
|
||||
emby_dbitem = emby_db.getItem_byId(itemid)
|
||||
try:
|
||||
mvideoid = emby_dbitem[0]
|
||||
fileid = emby_dbitem[1]
|
||||
log.info("Update playstate for musicvideo: %s fileid: %s", item['Name'], fileid)
|
||||
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'] = Local(obj['DatePlayed']).split('.')[0].replace('T', " ")
|
||||
|
||||
if obj['Favorite']:
|
||||
self.get_tag(*values(obj, QU.get_tag_mvideo_obj))
|
||||
else:
|
||||
self.kodi_db.remove_tag(mvideoid, "Favorite musicvideos", "musicvideo")
|
||||
self.remove_tag(*values(obj, QU.delete_tag_mvideo_obj))
|
||||
|
||||
# Process playstates
|
||||
playcount = userdata['PlayCount']
|
||||
dateplayed = userdata['LastPlayedDate']
|
||||
resume = API.adjust_resume(userdata['Resume'])
|
||||
total = round(float(runtime), 6)
|
||||
self.add_playstate(*values(obj, QU.add_bookmark_obj))
|
||||
self.emby_db.update_reference(*values(obj, QUEM.update_reference_obj))
|
||||
LOG.info("USERDATA mvideo [%s/%s] %s: %s", obj['FileId'], obj['MvideoId'], obj['Id'], obj['Title'])
|
||||
|
||||
self.kodi_db.add_playstate(fileid, resume, total, playcount, dateplayed)
|
||||
emby_db.updateReference(itemid, checksum)
|
||||
@stop()
|
||||
@emby_item()
|
||||
def remove(self, item_id, e_item):
|
||||
|
||||
def remove(self, itemid):
|
||||
# Remove mvideoid, fileid, pathid, emby reference
|
||||
emby_db = self.emby_db
|
||||
kodicursor = self.kodicursor
|
||||
artwork = self.artwork
|
||||
''' Remove mvideoid, fileid, pathid, emby reference.
|
||||
'''
|
||||
obj = {'Id': item_id}
|
||||
|
||||
emby_dbitem = emby_db.getItem_byId(itemid)
|
||||
try:
|
||||
mvideoid = emby_dbitem[0]
|
||||
fileid = emby_dbitem[1]
|
||||
pathid = emby_dbitem[2]
|
||||
log.info("Removing mvideoid: %s fileid: %s pathid: %s", mvideoid, fileid, pathid)
|
||||
obj['MvideoId'] = e_item[0]
|
||||
obj['FileId'] = e_item[1]
|
||||
obj['PathId'] = e_item[2]
|
||||
except TypeError:
|
||||
return
|
||||
|
||||
# Remove the emby reference
|
||||
emby_db.removeItem(itemid)
|
||||
# Remove artwork
|
||||
artwork.delete_artwork(mvideoid, "musicvideo", self.kodicursor)
|
||||
self.artwork.delete(obj['MvideoId'], "musicvideo")
|
||||
self.delete(*values(obj, QU.delete_musicvideo_obj))
|
||||
|
||||
self.kodi_db.remove_musicvideo(mvideoid, fileid)
|
||||
if self.direct_path:
|
||||
self.kodi_db.remove_path(pathid)
|
||||
self.remove_path(*values(obj, QU.delete_path_obj))
|
||||
|
||||
log.info("Deleted musicvideo %s from kodi database", itemid)
|
||||
self.emby_db.remove_item(*values(obj, QUEM.delete_item_obj))
|
||||
LOG.info("DELETE musicvideo %s [%s/%s] %s", obj['MvideoId'], obj['PathId'], obj['FileId'], obj['Id'])
|
||||
|
|
161
resources/lib/objects/obj.py
Normal file
161
resources/lib/objects/obj.py
Normal file
|
@ -0,0 +1,161 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
##################################################################################################
|
||||
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
|
||||
##################################################################################################
|
||||
|
||||
LOG = logging.getLogger("EMBY."+__name__)
|
||||
|
||||
##################################################################################################
|
||||
|
||||
|
||||
class Objects(object):
|
||||
|
||||
# Borg - multiple instances, shared state
|
||||
_shared_state = {}
|
||||
|
||||
def __init__(self):
|
||||
|
||||
''' Hold all persistent data here.
|
||||
'''
|
||||
|
||||
self.__dict__ = self._shared_state
|
||||
|
||||
def mapping(self):
|
||||
|
||||
''' Load objects mapping.
|
||||
'''
|
||||
with open(os.path.join(os.path.dirname(__file__), 'obj_map.json')) as infile:
|
||||
self.objects = json.load(infile)
|
||||
|
||||
def map(self, item, mapping_name):
|
||||
|
||||
''' Syntax to traverse the item dictionary.
|
||||
This of the query almost as a url.
|
||||
|
||||
Item is the Emby item json object structure
|
||||
|
||||
",": each element will be used as a fallback until a value is found.
|
||||
"?": split filters and key name from the query part, i.e. MediaSources/0?$Name
|
||||
"$": lead the key name with $. Only one key value can be requested per element.
|
||||
":": indicates it's a list of elements [], i.e. MediaSources/0/MediaStreams:?$Name
|
||||
MediaStreams is a list.
|
||||
"/": indicates where to go directly
|
||||
'''
|
||||
self.mapped_item = {}
|
||||
|
||||
if not mapping_name:
|
||||
raise Exception("execute mapping() first")
|
||||
|
||||
mapping = self.objects[mapping_name]
|
||||
|
||||
for key, value in mapping.iteritems():
|
||||
|
||||
self.mapped_item[key] = None
|
||||
params = value.split(',')
|
||||
|
||||
for param in params:
|
||||
|
||||
obj = item
|
||||
obj_param = param
|
||||
obj_key = ""
|
||||
obj_filters = {}
|
||||
|
||||
if '?' in obj_param:
|
||||
|
||||
if '$' in obj_param:
|
||||
obj_param, obj_key = obj_param.rsplit('$', 1)
|
||||
|
||||
obj_param, filters = obj_param.rsplit('?', 1)
|
||||
|
||||
if filters:
|
||||
for filter in filters.split('&'):
|
||||
filter_key, filter_value = filter.split('=')
|
||||
obj_filters[filter_key] = filter_value
|
||||
|
||||
if ':' in obj_param:
|
||||
result = []
|
||||
|
||||
for d in self.__recursiveloop__(obj, obj_param):
|
||||
|
||||
if obj_filters and self.__filters__(d, obj_filters):
|
||||
result.append(d)
|
||||
elif not obj_filters:
|
||||
result.append(d)
|
||||
|
||||
obj = result
|
||||
obj_filters = {}
|
||||
|
||||
elif '/' in obj_param:
|
||||
obj = self.__recursive__(obj, obj_param)
|
||||
|
||||
elif obj is item and obj is not None:
|
||||
obj = item.get(obj_param)
|
||||
|
||||
if obj_filters and obj:
|
||||
if not self.__filters__(obj, obj_filters):
|
||||
obj = None
|
||||
|
||||
if obj is None 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
|
||||
|
||||
if not mapping_name.startswith('Browse') and not mapping_name.startswith('Artwork') and not mapping_name.startswith('UpNext'):
|
||||
|
||||
self.mapped_item['ProviderName'] = self.objects.get('%sProviderName' % mapping_name)
|
||||
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
|
363
resources/lib/objects/obj_map.json
Normal file
363
resources/lib/objects/obj_map.json
Normal file
|
@ -0,0 +1,363 @@
|
|||
{
|
||||
"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",
|
||||
"EmbyParentId": "ParentId",
|
||||
"CriticRating": "CriticRating"
|
||||
},
|
||||
"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",
|
||||
"EmbyParentId": "ParentId",
|
||||
"Status": "Status"
|
||||
},
|
||||
"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",
|
||||
"EmbyParentId": "SeriesId,ParentId"
|
||||
},
|
||||
"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",
|
||||
"EmbyParentId": "ParentId"
|
||||
},
|
||||
"MusicVideoUserData": {
|
||||
"Id": "Id",
|
||||
"Title": "Name",
|
||||
"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",
|
||||
"EmbyParentId": "ParentId"
|
||||
},
|
||||
"Album": {
|
||||
"Id": "Id",
|
||||
"Title": "Name",
|
||||
"UniqueId": "ProviderIds/MusicBrainzAlbum",
|
||||
"Year": "ProductionYear",
|
||||
"Genres": "Genres",
|
||||
"Bio": "Overview",
|
||||
"AlbumArtists": "AlbumArtists",
|
||||
"Artists": "AlbumArtists:?$Name",
|
||||
"ArtistItems": "ArtistItems",
|
||||
"EmbyParentId": "ParentId"
|
||||
},
|
||||
"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",
|
||||
"EmbyParentId": "ParentId"
|
||||
},
|
||||
"SongUserData": {
|
||||
"Id": "Id",
|
||||
"Title": "Name",
|
||||
"PlayCount": "UserData/PlayCount",
|
||||
"DatePlayed": "UserData/LastPlayedDate",
|
||||
"DateAdded": "DateCreated",
|
||||
"Played": "UserData/Played"
|
||||
},
|
||||
"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",
|
||||
"CriticRating": "CriticRating",
|
||||
"Status": "Status"
|
||||
},
|
||||
"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"
|
||||
},
|
||||
"BrowseChannel": {
|
||||
"Id": "Id",
|
||||
"Title": "Name",
|
||||
"Type": "Type",
|
||||
"ProgramName": "CurrentProgram/Name",
|
||||
"Played": "CurrentProgram/UserData/Played",
|
||||
"PlayCount": "CurrentProgram/UserData/PlayCount",
|
||||
"Runtime": "CurrentProgram/RunTimeTicks",
|
||||
"MediaType": "MediaType"
|
||||
},
|
||||
"UpNext": {
|
||||
"episodeid": "Id",
|
||||
"tvshowid": "SeriesId",
|
||||
"plot": "Overview",
|
||||
"showtitle": "SeriesName",
|
||||
"title": "Name",
|
||||
"playcount": "UserData/PlayCount",
|
||||
"season": "ParentIndexNumber",
|
||||
"episode": "IndexNumber",
|
||||
"rating": "CommunityRating",
|
||||
"firstaired": "ProductionYear"
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load diff
37
resources/lib/objects/utils.py
Normal file
37
resources/lib/objects/utils.py
Normal file
|
@ -0,0 +1,37 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
#################################################################################################
|
||||
|
||||
import logging
|
||||
|
||||
from helper import JSONRPC
|
||||
|
||||
#################################################################################################
|
||||
|
||||
LOG = logging.getLogger("EMBY."+__name__)
|
||||
|
||||
#################################################################################################
|
||||
|
||||
def get_play_action():
|
||||
|
||||
''' I could not figure out a way to listen to kodi setting changes?
|
||||
For now, verify the play action every time play is called.
|
||||
'''
|
||||
options = ['Choose', 'Play', 'Resume', 'Show information']
|
||||
result = JSONRPC('Settings.GetSettingValue').execute({'setting': "myvideos.selectaction"})
|
||||
try:
|
||||
return options[result['result']['value']]
|
||||
except Exception as error:
|
||||
log.error("Returning play action due to error: %s", error)
|
||||
|
||||
return options[1]
|
||||
|
||||
def get_grouped_set():
|
||||
|
||||
''' Get if boxsets should be grouped
|
||||
'''
|
||||
result = JSONRPC('Settings.GetSettingValue').execute({'setting': "videolibrary.groupmoviesets"})
|
||||
try:
|
||||
return result['result']['value']
|
||||
except Exception as error:
|
||||
return False
|
Loading…
Add table
Add a link
Reference in a new issue