Remove old code

This commit is contained in:
angelblue05 2018-09-06 14:41:02 -05:00
parent e4e5881ffc
commit ce47644868
10 changed files with 6 additions and 1761 deletions

View file

@ -28,7 +28,7 @@ LOG = logging.getLogger("EMBY.context")
if __name__ == "__main__":
LOG.info("--->[ context ]")
LOG.debug("--->[ context ]")
try:
Context()

View file

@ -28,7 +28,7 @@ LOG = logging.getLogger("EMBY.context")
if __name__ == "__main__":
LOG.info("--->[ context ]")
LOG.debug("--->[ context ]")
try:
Context(True)

View file

@ -28,7 +28,7 @@ LOG = logging.getLogger("EMBY.default")
if __name__ == "__main__":
LOG.info("--->[ default ]")
LOG.debug("--->[ default ]")
try:
Events()
@ -36,197 +36,3 @@ if __name__ == "__main__":
LOG.exception(error)
LOG.info("---<[ default ]")
"""
# -*- coding: utf-8 -*-
#################################################################################################
import logging
import os
import sys
import urlparse
import xbmc
import xbmcaddon
import xbmcplugin
#################################################################################################
_ADDON = xbmcaddon.Addon(id='plugin.video.emby')
_CWD = _ADDON.getAddonInfo('path').decode('utf-8')
_BASE_LIB = xbmc.translatePath(os.path.join(_CWD, 'resources', 'lib')).decode('utf-8')
sys.path.append(_BASE_LIB)
#################################################################################################
import entrypoint
import loghandler
from utils import window, dialog, language as lang
from ga_client import GoogleAnalytics
import database
#################################################################################################
loghandler.config()
log = logging.getLogger("EMBY.default")
#################################################################################################
class Main(object):
# MAIN ENTRY POINT
#@utils.profiling()
def __init__(self):
# Parse parameters
base_url = sys.argv[0]
path = sys.argv[2]
params = urlparse.parse_qs(path[1:])
log.warn("Parameter string: %s params: %s", path, params)
try:
mode = params['mode'][0]
except (IndexError, KeyError):
mode = ""
if "/extrafanart" in base_url:
emby_path = path[1:]
emby_id = params.get('id', [""])[0]
entrypoint.getExtraFanArt(emby_id, emby_path)
elif "/Extras" in base_url or "/VideoFiles" in base_url:
emby_path = path[1:]
emby_id = params.get('id', [""])[0]
entrypoint.getVideoFiles(emby_id, emby_path)
elif not self._modes(mode, params):
# Other functions
if mode == 'settings':
xbmc.executebuiltin('Addon.OpenSettings(plugin.video.emby)')
elif mode in ('manualsync', 'fastsync', 'repair', 'refreshboxsets'):
self._library_sync(mode)
elif mode == 'texturecache':
import artwork
artwork.Artwork().texture_cache_sync()
else:
entrypoint.doMainListing()
if sys.argv:
xbmcplugin.endOfDirectory(int(sys.argv[1]))
@classmethod
def _modes(cls, mode, params):
import utils
modes = {
'reset': database.db_reset,
'resetauth': entrypoint.resetAuth,
'play': entrypoint.doPlayback,
'passwords': utils.passwordsXML,
'adduser': entrypoint.addUser,
'thememedia': entrypoint.getThemeMedia,
'channels': entrypoint.BrowseChannels,
'channelsfolder': entrypoint.BrowseChannels,
'browsecontent': entrypoint.BrowseContent,
'getsubfolders': entrypoint.GetSubFolders,
'nextup': entrypoint.getNextUpEpisodes,
'inprogressepisodes': entrypoint.getInProgressEpisodes,
'recentepisodes': entrypoint.getRecentEpisodes,
'refreshplaylist': entrypoint.refreshPlaylist,
'deviceid': entrypoint.resetDeviceId,
'delete': entrypoint.deleteItem,
'connect': entrypoint.emby_connect,
'backup': entrypoint.emby_backup,
'manuallogin': entrypoint.test_manual_login,
'connectlogin': entrypoint.test_connect_login,
'manualserver': entrypoint.test_manual_server,
'connectservers': entrypoint.test_connect_servers,
'connectusers': entrypoint.test_connect_users
}
if mode in modes:
# Simple functions
action = modes[mode]
item_id = params.get('id')
if item_id:
item_id = item_id[0]
if mode == 'play':
database_id = params.get('dbid')
action(item_id, database_id)
elif mode == 'recentepisodes':
limit = int(params['limit'][0])
action(item_id, limit, params.get('filters', [""])[0])
elif mode in ('nextup', 'inprogressepisodes'):
limit = int(params['limit'][0])
action(item_id, limit)
elif mode in ('channels', 'getsubfolders'):
action(item_id)
elif mode == 'browsecontent':
action(item_id, params.get('type', [""])[0], params.get('folderid', [""])[0])
elif mode == 'channelsfolder':
folderid = params['folderid'][0]
action(item_id, folderid)
else:
action()
return True
return False
@classmethod
def _library_sync(cls, mode):
if window('emby_online') != "true":
# Server is not online, do not run the sync
dialog(type_="ok",
heading="{emby}",
line1=lang(33034))
log.warn("Not connected to the emby server")
elif window('emby_dbScan') != "true":
import librarysync
library_sync = librarysync.LibrarySync()
if mode == 'manualsync':
librarysync.ManualSync().sync()
elif mode == 'fastsync':
library_sync.startSync()
elif mode == 'refreshboxsets':
librarysync.ManualSync().sync('boxsets')
else:
library_sync.fullSync(repair=True)
else:
log.warn("Database scan is already running")
if __name__ == "__main__":
log.info("plugin.video.emby started")
try:
Main()
except Exception as error:
if not (hasattr(error, 'quiet') and error.quiet):
ga = GoogleAnalytics()
errStrings = ga.formatException()
ga.sendEventData("Exception", errStrings[0], errStrings[1])
log.exception(error)
raise
log.info("plugin.video.emby stopped")
"""

View file

@ -315,488 +315,3 @@ class FullSync(object):
obj.boxsets_reset()
self.boxsets()
"""
# -*- coding: utf-8 -*-
##################################################################################################
import logging
import sqlite3
import threading
from datetime import datetime, timedelta, time
import xbmc
import xbmcgui
import xbmcvfs
import api
import utils
import clientinfo
import database
import downloadutils
import itemtypes
import emby_api as mb
import embydb_functions as embydb
import read_embyserver as embyserver
import userclient
import views
from objects import Movies, MusicVideos, TVShows, Music
from utils import window, settings, language as lang, should_stop
from ga_client import GoogleAnalytics
##################################################################################################
log = logging.getLogger("EMBY."+__name__)
##################################################################################################
class LibrarySync(threading.Thread):
_shared_state = {}
isFastSync = False
stop_thread = False
suspend_thread = False
# Track websocketclient updates
addedItems = []
updateItems = []
userdataItems = []
removeItems = []
forceLibraryUpdate = False
incremental_count = 0
refresh_views = False
def __init__(self):
self.__dict__ = self._shared_state
self.monitor = xbmc.Monitor()
self.clientInfo = clientinfo.ClientInfo()
self.doUtils = downloadutils.DownloadUtils().downloadUrl
self.user = userclient.UserClient()
self.emby = embyserver.Read_EmbyServer()
self.kodi_version = int(xbmc.getInfoLabel('System.BuildVersion')[:2])
threading.Thread.__init__(self)
def progressDialog(self, title):
dialog = None
dialog = xbmcgui.DialogProgressBG()
dialog.create("Emby for Kodi", title)
log.debug("Show progress dialog: %s" % title)
return dialog
def saveLastSync(self):
# Save last sync time
overlap = 2
try: # datetime fails when used more than once, TypeError
if self.isFastSync:
result = self.doUtils("{server}/emby/Emby.Kodi.SyncQueue/GetServerDateTime?format=json")
server_time = result['ServerDateTime']
server_time = utils.convertDate(server_time)
else:
raise Exception("Fast sync server plugin is not enabled.")
except Exception as e:
# If the server plugin is not installed or an error happened.
log.debug("An exception occurred: %s" % e)
time_now = datetime.utcnow()-timedelta(minutes=overlap)
lastSync = time_now.strftime('%Y-%m-%dT%H:%M:%SZ')
log.info("New sync time: client time -%s min: %s" % (overlap, lastSync))
else:
lastSync = (server_time - timedelta(minutes=overlap)).strftime('%Y-%m-%dT%H:%M:%SZ')
log.info("New sync time: server time -%s min: %s" % (overlap, lastSync))
finally:
settings('LastIncrementalSync', value=lastSync)
def fullSync(self, manualrun=False, repair=False):
# Only run once when first setting up. Can be run manually.
music_enabled = settings('enableMusic') == "true"
xbmc.executebuiltin('InhibitIdleShutdown(true)')
screensaver = utils.getScreensaver()
utils.setScreensaver(value="")
window('emby_dbScan', value="true")
# Add sources
utils.sourcesXML()
# use emby and video DBs
with database.DatabaseConn('emby') as cursor_emby:
with database.DatabaseConn('video') as cursor_video:
# content sync: movies, tvshows, musicvideos, music
if manualrun:
message = "Manual sync"
elif repair:
message = "Repair sync"
repair_list = []
choices = ['all', 'movies', 'musicvideos', 'tvshows']
if music_enabled:
choices.append('music')
if self.kodi_version > 15:
# Jarvis or higher
types = xbmcgui.Dialog().multiselect(lang(33094), choices)
if types is None:
pass
elif 0 in types: # all
choices.pop(0)
repair_list.extend(choices)
else:
for index in types:
repair_list.append(choices[index])
else:
resp = xbmcgui.Dialog().select(lang(33094), choices)
if resp == 0: # all
choices.pop(resp)
repair_list.extend(choices)
else:
repair_list.append(choices[resp])
log.info("Repair queued for: %s", repair_list)
else:
message = "Initial sync"
window('emby_initialScan', value="true")
pDialog = self.progressDialog("%s" % message)
starttotal = datetime.now()
# Set views
views.Views(cursor_emby, cursor_video).maintain()
cursor_emby.connection.commit()
#self.maintainViews(cursor_emby, cursor_video)
# Sync video library
process = {
'movies': self.movies,
'boxsets': self.boxsets,
'musicvideos': self.musicvideos,
'tvshows': self.tvshows
}
for itemtype in process:
if repair and itemtype not in repair_list:
continue
startTime = datetime.now()
completed = process[itemtype](cursor_emby, cursor_video, pDialog)
if not completed:
xbmc.executebuiltin('InhibitIdleShutdown(false)')
utils.setScreensaver(value=screensaver)
window('emby_dbScan', clear=True)
if pDialog:
pDialog.close()
return False
else:
elapsedTime = datetime.now() - startTime
log.info("SyncDatabase (finished %s in: %s)"
% (itemtype, str(elapsedTime).split('.')[0]))
# sync music
# use emby and music
if music_enabled:
if repair and 'music' not in repair_list:
pass
else:
with database.DatabaseConn('emby') as cursor_emby:
with database.DatabaseConn('music') as cursor_music:
startTime = datetime.now()
completed = self.music(cursor_emby, cursor_music, pDialog)
if not completed:
xbmc.executebuiltin('InhibitIdleShutdown(false)')
utils.setScreensaver(value=screensaver)
window('emby_dbScan', clear=True)
if pDialog:
pDialog.close()
return False
else:
elapsedTime = datetime.now() - startTime
log.info("SyncDatabase (finished music in: %s)"
% (str(elapsedTime).split('.')[0]))
if pDialog:
pDialog.close()
with database.DatabaseConn('emby') as cursor_emby:
emby_db = embydb.Embydb_Functions(cursor_emby)
current_version = emby_db.get_version(self.clientInfo.get_version())
window('emby_version', current_version)
settings('SyncInstallRunDone', value="true")
self.saveLastSync()
xbmc.executebuiltin('UpdateLibrary(video)')
elapsedtotal = datetime.now() - starttotal
xbmc.executebuiltin('InhibitIdleShutdown(false)')
utils.setScreensaver(value=screensaver)
window('emby_dbScan', clear=True)
window('emby_initialScan', clear=True)
xbmcgui.Dialog().notification(
heading=lang(29999),
message="%s %s %s" %
(message, lang(33025), str(elapsedtotal).split('.')[0]),
icon="special://home/addons/plugin.video.emby/icon.png",
sound=False)
return True
def refreshViews(self):
with database.DatabaseConn('emby') as cursor_emby:
with database.DatabaseConn() as cursor_video:
# Compare views, assign correct tags to items
views.Views(cursor_emby, cursor_video).maintain()
def offline_mode_views(self):
with database.DatabaseConn('emby') as cursor_emby:
with database.DatabaseConn() as cursor_video:
views.Views(cursor_emby, cursor_video).offline_mode()
def compareDBVersion(self, current, minimum):
# It returns True is database is up to date. False otherwise.
log.info("current: %s minimum: %s" % (current, minimum))
try:
currMajor, currMinor, currPatch = current.split(".")
minMajor, minMinor, minPatch = minimum.split(".")
except ValueError as error:
raise ValueError("Unable to compare versions: %s, %s" % (current, minimum))
if currMajor > minMajor:
return True
elif currMajor == minMajor and (currMinor > minMinor or
(currMinor == minMinor and currPatch >= minPatch)):
return True
else:
# Database out of date.
return False
def run(self):
try:
self.run_internal()
except Warning as e:
if "restricted" in e:
pass
elif "401" in e:
pass
except Exception as e:
ga = GoogleAnalytics()
errStrings = ga.formatException()
if not (hasattr(e, 'quiet') and e.quiet):
ga.sendEventData("Exception", errStrings[0], errStrings[1])
window('emby_dbScan', clear=True)
log.exception(e)
xbmcgui.Dialog().ok(
heading=lang(29999),
line1=(
"Library sync thread has exited! "
"You should restart Kodi now. "
"Please report this on the forum."),
line2=(errStrings[0] + " (" + errStrings[1] + ")"))
def run_internal(self):
dialog = xbmcgui.Dialog()
startupComplete = False
log.warn("---===### Starting LibrarySync ###===---")
if utils.verify_advancedsettings():
# Advancedsettings was modified, Kodi needs to restart
log.warn("###===--- LibrarySync Aborted ---===###")
return
while not self.monitor.abortRequested():
# In the event the server goes offline
while self.suspend_thread:
# Set in service.py
if self.monitor.waitForAbort(5):
# Abort was requested while waiting. We should exit
break
if (window('emby_dbCheck') != "true" and settings('SyncInstallRunDone') == "true"):
# Verify the validity of the database
log.info("Doing DB Version Check")
with database.DatabaseConn('emby') as cursor:
emby_db = embydb.Embydb_Functions(cursor)
currentVersion = emby_db.get_version()
###$ Begin migration $###
if not currentVersion:
currentVersion = emby_db.get_version(settings('dbCreatedWithVersion') or self.clientInfo.get_version())
log.info("Migration of database version completed")
###$ End migration $###
window('emby_version', value=currentVersion)
minVersion = window('emby_minDBVersion')
uptoDate = self.compareDBVersion(currentVersion, minVersion)
if not uptoDate:
log.warn("Database version out of date: %s minimum version required: %s"
% (currentVersion, minVersion))
resp = dialog.yesno(lang(29999), lang(33022))
if not resp:
log.warn("Database version is out of date! USER IGNORED!")
dialog.ok(lang(29999), lang(33023))
else:
database.db_reset()
break
window('emby_dbCheck', value="true")
if not startupComplete:
# Verify the video database can be found
videoDb = database.video_database()
if not xbmcvfs.exists(videoDb):
# Database does not exists
log.error(
"The current Kodi version is incompatible "
"with the Emby for Kodi add-on. Please visit "
"https://github.com/MediaBrowser/Emby.Kodi/wiki "
"to know which Kodi versions are supported.")
dialog.ok(
heading=lang(29999),
line1=lang(33024))
break
# Run start up sync
log.warn("Database version: %s", window('emby_version'))
log.info("SyncDatabase (started)")
startTime = datetime.now()
librarySync = self.startSync()
elapsedTime = datetime.now() - startTime
log.info("SyncDatabase (finished in: %s) %s"
% (str(elapsedTime).split('.')[0], librarySync))
# Add other servers at this point
# TODO: re-add once plugin listing is created
# self.user.load_connect_servers()
# Only try the initial sync once per kodi session regardless
# This will prevent an infinite loop in case something goes wrong.
startupComplete = True
# Process updates
if self.incremental_count > 5:
self.incremental_count = 0
window('emby_kodiScan', clear=True)
if ((not xbmc.Player().isPlayingVideo() or xbmc.getCondVisibility('VideoPlayer.Content(livetv)')) and
window('emby_dbScan') != "true" and window('emby_shouldStop') != "true"):
self.incrementalSync()
if window('emby_onWake') == "true" and window('emby_online') == "true":
# Kodi is waking up
# Set in kodimonitor.py
window('emby_onWake', clear=True)
if window('emby_syncRunning') != "true":
log.info("SyncDatabase onWake (started)")
librarySync = self.startSync()
log.info("SyncDatabase onWake (finished) %s" % librarySync)
if self.stop_thread:
# Set in service.py
log.debug("Service terminated thread.")
break
if self.monitor.waitForAbort(1):
# Abort was requested while waiting. We should exit
break
log.warn("###===--- LibrarySync Stopped ---===###")
def stopThread(self):
self.stop_thread = True
log.debug("Ending thread...")
def suspendThread(self):
self.suspend_thread = True
log.debug("Pausing thread...")
def resumeThread(self):
self.suspend_thread = False
log.debug("Resuming thread...")
class ManualSync(LibrarySync):
def __init__(self):
LibrarySync.__init__(self)
def sync(self, mediatype=None):
if mediatype in ('movies', 'boxsets', 'musicvideos', 'tvshows'):
with database.DatabaseConn('emby') as cursor_emby:
with database.DatabaseConn('video') as cursor_video:
pDialog = self.progressDialog("Manual Sync: %s" % mediatype)
if mediatype == 'movies':
self.movies(cursor_emby, cursor_video, pDialog)
elif mediatype == "boxsets":
self.boxsets(cursor_emby, cursor_video, pDialog)
elif mediatype =='musicvideos':
self.musicvideos(cursor_emby, cursor_video, pDialog)
elif mediatype == 'tvshows':
self.tvshows(cursor_emby, cursor_video, pDialog)
pDialog.close()
return
elif mediatype == 'music':
with database.DatabaseConn('emby') as cursor_emby:
with database.DatabaseConn('music') as cursor_music:
pDialog = self.progressDialog("Manual Sync: %s" % mediatype)
self.music(cursor_emby, cursor_music, pDialog)
pDialog.close()
return
else:
return self.fullSync(manualrun=True)
def movies(self, embycursor, kodicursor, pdialog):
return Movies(embycursor, kodicursor, pdialog).compare_all()
def boxsets(self, embycursor, kodicursor, pdialog):
return Movies(embycursor, kodicursor, pdialog).force_refresh_boxsets()
def musicvideos(self, embycursor, kodicursor, pdialog):
return MusicVideos(embycursor, kodicursor, pdialog).compare_all()
def tvshows(self, embycursor, kodicursor, pdialog):
return TVShows(embycursor, kodicursor, pdialog).compare_all()
def music(self, embycursor, kodicursor, pdialog):
return Music(embycursor, kodicursor).compare_all()
"""

View file

@ -33,80 +33,6 @@ class API(object):
'''
return (playcount or 1) if played else None
def get_userdata(self):
# Default
favorite = False
likes = None
playcount = None
played = False
last_played = None
resume = 0
try:
userdata = self.item['UserData']
except KeyError: # No userdata found.
pass
else:
favorite = userdata['IsFavorite']
likes = userdata.get('Likes')
last_played = userdata.get('LastPlayedDate')
if last_played:
last_played = last_played.split('.')[0].replace('T', " ")
if userdata['Played']:
# Playcount is tied to the watch status
played = True
playcount = userdata['PlayCount']
if playcount == 0:
playcount = 1
if last_played is None:
last_played = self.get_date_created()
playback_position = userdata.get('PlaybackPositionTicks')
if playback_position:
resume = playback_position / 10000000.0
return {
'Favorite': favorite,
'Likes': likes,
'PlayCount': playcount,
'Played': played,
'LastPlayedDate': last_played,
'Resume': resume
}
def get_people(self):
# Process People
director = []
writer = []
cast = []
if 'People' in self.item:
for person in self.item['People']:
type_ = person['Type']
name = person['Name']
if type_ == 'Director':
director.append(name)
elif type_ == 'Actor':
cast.append(name)
elif type_ in ('Writing', 'Writer'):
writer.append(name)
return {
'Director': director,
'Writer': writer,
'Cast': cast
}
def get_actors(self):
cast = []
@ -125,40 +51,6 @@ class API(object):
return cast
def get_media_streams(self):
video_tracks = []
audio_tracks = []
subtitle_languages = []
try:
media_streams = self.item['MediaSources'][0]['MediaStreams']
except KeyError:
if not self.item.get("MediaStreams"):
return None
media_streams = self.item['MediaStreams']
for media_stream in media_streams:
# Sort through Video, Audio, Subtitle
stream_type = media_stream['Type']
if stream_type == "Video":
self._video_stream(video_tracks, media_stream)
elif stream_type == "Audio":
self._audio_stream(audio_tracks, media_stream)
elif stream_type == "Subtitle":
subtitle_languages.append(media_stream.get('Language', "Unknown"))
return {
'video': video_tracks,
'audio': audio_tracks,
'subtitle': subtitle_languages
}
def media_streams(self, video, audio, subtitles):
return {
'video': video or [],
@ -247,21 +139,6 @@ class API(object):
return resume
def get_studios(self):
# Process Studios
studios = []
try:
studio = self.item['SeriesStudio']
studios.append(self.validate_studio(studio))
except KeyError:
for studio in self.item['Studios']:
name = studio['Name']
studios.append(self.validate_studio(name))
return studios
def validate_studio(self, studio_name):
# Convert studio for Kodi to properly detect them
studios = {
@ -277,36 +154,6 @@ class API(object):
}
return studios.get(studio_name.lower(), studio_name)
def get_genres(self):
all_genres = ""
genres = self.item.get('Genres', self.item.get('SeriesGenres'))
if genres:
all_genres = " / ".join(genres)
return all_genres
def get_date_created(self):
try:
date_added = self.item['DateCreated']
date_added = date_added.split('.')[0].replace('T', " ")
except KeyError:
date_added = None
return date_added
def get_premiere_date(self):
try:
premiere = self.item['PremiereDate']
premiere = premiere.split('.')[0].replace('T', " ")
except KeyError:
premiere = None
return premiere
def get_overview(self, overview=None):
overview = overview or self.item.get('Overview')
@ -321,24 +168,6 @@ class API(object):
return overview
def get_tagline(self):
try:
tagline = self.item['Taglines'][0]
except IndexError:
tagline = None
return tagline
def get_provider(self, name):
try:
provider = self.item['ProviderIds'][name]
except KeyError:
provider = None
return provider
def get_mpaa(self):
# Convert more complex cases
mpaa = self.item.get('OfficialRating', "")
@ -352,15 +181,6 @@ class API(object):
return mpaa
def get_country(self):
try:
country = self.item['ProductionLocations'][0]
except (IndexError, KeyError):
country = None
return country
def get_file_path(self, path=None):
if path is None:

View file

@ -273,117 +273,3 @@ def normalize_string(text):
text = unicodedata.normalize('NFKD', unicode(text, 'utf-8')).encode('ascii', 'ignore')
return text
"""
#################################################################################################
# Utility methods
def getScreensaver():
# Get the current screensaver value
result = JSONRPC('Settings.getSettingValue').execute({'setting': "screensaver.mode"})
try:
return result['result']['value']
except KeyError:
return ""
def setScreensaver(value):
# Toggle the screensaver
params = {
'setting': "screensaver.mode",
'value': value
}
result = JSONRPC('Settings.setSettingValue').execute(params)
log.info("Toggling screensaver: %s %s" % (value, result))
def convertDate(date):
try:
date = datetime.strptime(date, "%Y-%m-%dT%H:%M:%SZ")
except (ImportError, TypeError):
# TypeError: attribute of type 'NoneType' is not callable
# Known Kodi/python error
date = datetime(*(time.strptime(date, "%Y-%m-%dT%H:%M:%SZ")[0:6]))
return date
def indent(elem, level=0):
# Prettify xml trees
i = "\n" + level*" "
if len(elem):
if not elem.text or not elem.text.strip():
elem.text = i + " "
if not elem.tail or not elem.tail.strip():
elem.tail = i
for elem in elem:
indent(elem, level+1)
if not elem.tail or not elem.tail.strip():
elem.tail = i
else:
if level and (not elem.tail or not elem.tail.strip()):
elem.tail = i
def profiling(sortby="cumulative"):
# Will print results to Kodi log
def decorator(func):
def wrapper(*args, **kwargs):
import cProfile
import pstats
pr = cProfile.Profile()
pr.enable()
result = func(*args, **kwargs)
pr.disable()
s = StringIO.StringIO()
ps = pstats.Stats(pr, stream=s).sort_stats(sortby)
ps.print_stats()
log.info(s.getvalue())
return result
return wrapper
return decorator
#################################################################################################
# Addon utilities
def verify_advancedsettings():
# Track the existance of <cleanonupdate>true</cleanonupdate>
# incompatible with plugin paths
log.info("verifying advanced settings")
if settings('useDirectPaths') != "0": return
path = xbmc.translatePath("special://userdata/").decode('utf-8')
xmlpath = "%sadvancedsettings.xml" % path
try:
xmlparse = etree.parse(xmlpath)
except: # Document is blank or missing
return
else:
root = xmlparse.getroot()
video = root.find('videolibrary')
if video is not None:
cleanonupdate = video.find('cleanonupdate')
if cleanonupdate is not None and cleanonupdate.text == "true":
log.warn("cleanonupdate disabled")
video.remove(cleanonupdate)
try:
indent(root)
except: pass
etree.ElementTree(root).write(xmlpath)
xbmcgui.Dialog().ok(heading=language(29999), line1=language(33097))
xbmc.executebuiltin('RestartApp')
return True
return
"""

View file

@ -80,272 +80,7 @@ def tvtunes_nfo(path, urls):
indent(xml)
write_xml(etree.tostring(xml, 'UTF-8'), path)
"""
# -*- coding: utf-8 -*-
#################################################################################################
import inspect
import json
import logging
import sqlite3
import StringIO
import os
import sys
import time
import urllib
import unicodedata
import xml.etree.ElementTree as etree
from datetime import datetime
from uuid import uuid4
import xbmc
import xbmcaddon
import xbmcgui
import xbmcplugin
import xbmcvfs
#################################################################################################
log = logging.getLogger("EMBY."+__name__)
#################################################################################################
#################################################################################################
# Utility methods
def getScreensaver():
# Get the current screensaver value
result = JSONRPC('Settings.getSettingValue').execute({'setting': "screensaver.mode"})
try:
return result['result']['value']
except KeyError:
return ""
def setScreensaver(value):
# Toggle the screensaver
params = {
'setting': "screensaver.mode",
'value': value
}
result = JSONRPC('Settings.setSettingValue').execute(params)
log.info("Toggling screensaver: %s %s" % (value, result))
def convertDate(date):
try:
date = datetime.strptime(date, "%Y-%m-%dT%H:%M:%SZ")
except (ImportError, TypeError):
# TypeError: attribute of type 'NoneType' is not callable
# Known Kodi/python error
date = datetime(*(time.strptime(date, "%Y-%m-%dT%H:%M:%SZ")[0:6]))
return date
def normalize_string(text):
# For theme media, do not modify unless
# modified in TV Tunes
text = text.replace(":", "")
text = text.replace("/", "-")
text = text.replace("\\", "-")
text = text.replace("<", "")
text = text.replace(">", "")
text = text.replace("*", "")
text = text.replace("?", "")
text = text.replace('|', "")
text = text.strip()
# Remove dots from the last character as windows can not have directories
# with dots at the end
text = text.rstrip('.')
text = unicodedata.normalize('NFKD', unicode(text, 'utf-8')).encode('ascii', 'ignore')
return text
def indent(elem, level=0):
# Prettify xml trees
i = "\n" + level*" "
if len(elem):
if not elem.text or not elem.text.strip():
elem.text = i + " "
if not elem.tail or not elem.tail.strip():
elem.tail = i
for elem in elem:
indent(elem, level+1)
if not elem.tail or not elem.tail.strip():
elem.tail = i
else:
if level and (not elem.tail or not elem.tail.strip()):
elem.tail = i
def profiling(sortby="cumulative"):
# Will print results to Kodi log
def decorator(func):
def wrapper(*args, **kwargs):
import cProfile
import pstats
pr = cProfile.Profile()
pr.enable()
result = func(*args, **kwargs)
pr.disable()
s = StringIO.StringIO()
ps = pstats.Stats(pr, stream=s).sort_stats(sortby)
ps.print_stats()
log.info(s.getvalue())
return result
return wrapper
return decorator
#################################################################################################
# Addon utilities
def sourcesXML():
# To make Master lock compatible
path = xbmc.translatePath("special://profile/").decode('utf-8')
xmlpath = "%ssources.xml" % path
try:
xmlparse = etree.parse(xmlpath)
except: # Document is blank or missing
root = etree.Element('sources')
else:
root = xmlparse.getroot()
video = root.find('video')
if video is None:
video = etree.SubElement(root, 'video')
etree.SubElement(video, 'default', attrib={'pathversion': "1"})
# Add elements
count = 2
for source in root.findall('.//path'):
if source.text == "smb://":
count -= 1
if count == 0:
# sources already set
break
else:
# Missing smb:// occurences, re-add.
for i in range(0, count):
source = etree.SubElement(video, 'source')
etree.SubElement(source, 'name').text = "Emby"
etree.SubElement(source, 'path', attrib={'pathversion': "1"}).text = "smb://"
etree.SubElement(source, 'allowsharing').text = "true"
# Prettify and write to file
try:
indent(root)
except: pass
etree.ElementTree(root).write(xmlpath)
def passwordsXML():
# To add network credentials
path = xbmc.translatePath("special://userdata/").decode('utf-8')
xmlpath = "%spasswords.xml" % path
try:
xmlparse = etree.parse(xmlpath)
except: # Document is blank or missing
root = etree.Element('passwords')
else:
root = xmlparse.getroot()
dialog = xbmcgui.Dialog()
credentials = settings('networkCreds')
if credentials:
# Present user with options
option = dialog.select(language(33075), [language(33076), language(33077)])
if option < 0:
# User cancelled dialog
return
elif option == 1:
# User selected remove
for paths in root.getiterator('passwords'):
for path in paths:
if path.find('.//from').text == "smb://%s/" % credentials:
paths.remove(path)
log.info("Successfully removed credentials for: %s" % credentials)
etree.ElementTree(root).write(xmlpath)
break
else:
log.info("Failed to find saved server: %s in passwords.xml" % credentials)
settings('networkCreds', value="")
xbmcgui.Dialog().notification(
heading=language(29999),
message="%s %s" % (language(33078), credentials),
icon="special://home/addons/plugin.video.emby/icon.png",
time=1000,
sound=False)
return
elif option == 0:
# User selected to modify
server = dialog.input(language(33083), credentials)
if not server:
return
else:
# No credentials added
dialog.ok(heading=language(29999), line1=language(33082))
server = dialog.input(language(33084))
if not server:
return
# Network username
user = dialog.input(language(33079))
if not user:
return
# Network password
password = dialog.input(heading=language(33080), option=xbmcgui.ALPHANUM_HIDE_INPUT)
if not password:
return
# Add elements
for path in root.findall('.//path'):
if path.find('.//from').text.lower() == "smb://%s/" % server.lower():
# Found the server, rewrite credentials
topath = "smb://%s:%s@%s/" % (user, password, server)
path.find('.//to').text = topath.replace("\\", ";")
break
else:
# Server not found, add it.
path = etree.SubElement(root, 'path')
etree.SubElement(path, 'from', attrib={'pathversion': "1"}).text = "smb://%s/" % server
topath = "smb://%s:%s@%s/" % (user, password, server)
etree.SubElement(path, 'to', attrib={'pathversion': "1"}).text = topath.replace("\\", ";")
# Force Kodi to see the credentials without restarting
xbmcvfs.exists(topath)
# Add credentials
settings('networkCreds', value=server)
log.info("Added server: %s to passwords.xml" % server)
# Prettify and write to file
try:
indent(root)
except: pass
etree.ElementTree(root).write(xmlpath)
dialog.notification(
heading=language(29999),
message="%s %s" % (language(33081), server),
icon="special://home/addons/plugin.video.emby/icon.png",
time=1000,
sound=False)
def verify_advancedsettings():
# Track the existance of <cleanonupdate>true</cleanonupdate>
# incompatible with plugin paths

View file

@ -364,519 +364,3 @@ class Player(xbmc.Player):
window('emby.external_check', clear=True)
self.played.clear()
"""
# -*- coding: utf-8 -*-
#################################################################################################
import json
import logging
import xbmc
import xbmcvfs
import xbmcgui
import clientinfo
import downloadutils
import read_embyserver as embyserver
import websocket_client as wsc
from utils import window, settings, language as lang, JSONRPC
from ga_client import GoogleAnalytics, log_error
#################################################################################################
log = logging.getLogger("EMBY."+__name__)
#################################################################################################
class Player(xbmc.Player):
# Borg - multiple instances, shared state
_shared_state = {}
played_info = {}
currentFile = None
def __init__(self):
self.__dict__ = self._shared_state
self.clientInfo = clientinfo.ClientInfo()
self.doUtils = downloadutils.DownloadUtils().downloadUrl
self.emby = embyserver.Read_EmbyServer()
self.ws = wsc.WebSocketClient()
self.xbmcplayer = xbmc.Player()
log.debug("Starting playback monitor.")
xbmc.Player.__init__(self)
def set_audio_subs(self, audio_index=None, subs_index=None):
''' Only for after playback started
'''
player = xbmc.Player()
log.info("Setting audio: %s subs: %s", audio_index, subs_index)
if audio_index and len(player.getAvailableAudioStreams()) > 1:
player.setAudioStream(audio_index - 1)
if subs_index:
mapping = window('emby_%s.indexMapping.json' % self.current_file)
if subs_index == -1:
player.showSubtitles(False)
elif mapping:
external_index = mapping
# If there's external subtitles added via playbackutils
for index in external_index:
if external_index[index] == subs_index:
player.setSubtitleStream(int(index))
break
else:
# User selected internal subtitles
external = len(external_index)
audio_tracks = len(player.getAvailableAudioStreams())
player.setSubtitleStream(external + subs_index - audio_tracks - 1)
else:
# Emby merges audio and subtitle index together
audio_tracks = len(player.getAvailableAudioStreams())
player.setSubtitleStream(subs_index - audio_tracks - 1)
@log_error()
def onPlayBackStarted(self):
# Will be called when xbmc starts playing a file
self.stopAll()
# Get current file
try:
currentFile = self.xbmcplayer.getPlayingFile()
xbmc.sleep(300)
except:
currentFile = ""
count = 0
while not currentFile:
xbmc.sleep(100)
try:
currentFile = self.xbmcplayer.getPlayingFile()
except: pass
if count == 5: # try 5 times
log.info("Cancelling playback report...")
break
else: count += 1
# if we did not get the current file return
if currentFile == "":
return
# process the playing file
self.currentFile = currentFile
# We may need to wait for info to be set in kodi monitor
item = window('emby_%s.json' % currentFile)
#itemId = window("emby_%s.itemid" % currentFile)
tryCount = 0
while not item:
xbmc.sleep(200)
item = window('emby_%s.json' % currentFile)
if tryCount == 20: # try 20 times or about 10 seconds
log.info("Could not find item, cancelling playback report...")
break
else: tryCount += 1
else:
item_id = item.get('id')
log.info("ONPLAYBACK_STARTED: %s itemid: %s", currentFile.decode('utf-8'), item_id)
# Only proceed if an itemId was found.
runtime = item.get('runtime')
refresh_id = item.get('refreshid')
play_method = item.get('playmethod')
item_type = item.get('type')
playsession_id = item.get('playsession_id')
mediasource_id = item.get('mediasource_id')
#self.set_audio_subs(item.get('forcedaudio'), item.get('forcedsubs'))
# Prevent manually mark as watched in Kodi monitor
window('emby.skip.%s' % item_id, value="true")
customseek = window('emby_customPlaylist.seektime')
if window('emby_customPlaylist') == "true" and customseek:
# Start at, when using custom playlist (play to Kodi from webclient)
log.info("Seeking to: %s", customseek)
self.xbmcplayer.seekTime(int(customseek)/10000000.0)
window('emby_customPlaylist.seektime', clear=True)
try:
seekTime = self.xbmcplayer.getTime()
except:
# at this point we should be playing and if not then bail out
return
# Get playback volume
result = JSONRPC('Application.GetProperties').execute({'properties': ["volume", "muted"]})
result = result.get('result')
volume = result.get('volume')
muted = result.get('muted')
# Postdata structure to send to Emby server
url = "{server}/emby/Sessions/Playing"
postdata = {
'QueueableMediaTypes': "Video,Audio",
'CanSeek': True,
'ItemId': item_id,
'MediaSourceId': mediasource_id or item_id,
'PlayMethod': play_method,
'VolumeLevel': volume,
'PositionTicks': int(seekTime * 10000000),
'IsMuted': muted,
'PlaySessionId': playsession_id
}
# Get the current audio track and subtitles
if play_method == "Transcode":
# property set in PlayUtils.py
postdata['AudioStreamIndex'] = window("%sAudioStreamIndex" % currentFile)
postdata['SubtitleStreamIndex'] = window("%sSubtitleStreamIndex" % currentFile)
else:
# Get the current kodi audio and subtitles and convert to Emby equivalent
params = {
'playerid': 1,
'properties': ["currentsubtitle","currentaudiostream","subtitleenabled"]
}
result = JSONRPC('Player.GetProperties').execute(params)
tracks_data = None
try:
tracks_data = json.loads(result)
tracks_data = tracks_data.get('result')
except:
tracks_data = None
try: # Audio tracks
indexAudio = tracks_data['currentaudiostream']['index']
except:
indexAudio = 0
try: # Subtitles tracks
indexSubs = tracks_data['currentsubtitle']['index']
except:
indexSubs = 0
try: # If subtitles are enabled
subsEnabled = tracks_data['subtitleenabled']
except:
subsEnabled = ""
# Postdata for the audio
postdata['AudioStreamIndex'] = indexAudio + 1
# Postdata for the subtitles
if subsEnabled and len(xbmc.Player().getAvailableSubtitleStreams()) > 0:
# Number of audiotracks to help get Emby Index
audioTracks = len(xbmc.Player().getAvailableAudioStreams())
mapping = window("emby_%s.indexMapping" % currentFile)
if mapping: # Set in playbackutils.py
log.debug("Mapping for external subtitles index: %s", mapping)
externalIndex = json.loads(mapping)
if externalIndex.get(str(indexSubs)):
# If the current subtitle is in the mapping
postdata['SubtitleStreamIndex'] = externalIndex[str(indexSubs)]
else:
# Internal subtitle currently selected
subindex = indexSubs - len(externalIndex) + audioTracks + 1
postdata['SubtitleStreamIndex'] = subindex
else: # Direct paths enabled scenario or no external subtitles set
postdata['SubtitleStreamIndex'] = indexSubs + audioTracks + 1
else:
postdata['SubtitleStreamIndex'] = ""
# Post playback to server
log.debug("Sending POST play started: %s.", postdata)
self.doUtils(url, postBody=postdata, action_type="POST")
# Ensure we do have a runtime
try:
runtime = int(runtime)
except ValueError:
try:
runtime = int(self.xbmcplayer.getTotalTime())
log.info("Runtime is missing, Kodi runtime: %s" % runtime)
except:
runtime = 0
log.info("Runtime is missing, Using Zero")
# Save data map for updates and position calls
data = {
'runtime': runtime,
'item_id': item_id,
'mediasource_id': mediasource_id,
'refresh_id': refresh_id,
'currentfile': currentFile,
'AudioStreamIndex': postdata['AudioStreamIndex'],
'SubtitleStreamIndex': postdata['SubtitleStreamIndex'],
'playmethod': play_method,
'Type': item_type,
'currentPosition': int(seekTime),
'playsession_id': playsession_id
}
self.played_info[currentFile] = data
log.info("ADDING_FILE: %s", self.played_info)
ga = GoogleAnalytics()
ga.sendEventData("PlayAction", item_type, play_method)
ga.sendScreenView(item_type)
def reportPlayback(self):
log.debug("reportPlayback Called")
# Get current file
currentFile = self.currentFile
data = self.played_info.get(currentFile)
# only report playback if emby has initiated the playback (item_id has value)
if data:
# Get playback information
itemId = data['item_id']
audioindex = data['AudioStreamIndex']
subtitleindex = data['SubtitleStreamIndex']
playTime = data['currentPosition']
playMethod = data['playmethod']
paused = data.get('paused', False)
playsession_id = data.get('playsession_id')
mediasource_id = data.get('mediasource_id')
# Get playback volume
result = JSONRPC('Application.GetProperties').execute({'properties': ["volume", "muted"]})
result = result.get('result')
volume = result.get('volume')
muted = result.get('muted')
# Postdata for the websocketclient report
postdata = {
'QueueableMediaTypes': "Video",
'CanSeek': True,
'ItemId': itemId,
'MediaSourceId': mediasource_id or itemId,
'PlayMethod': playMethod,
'PositionTicks': int(playTime * 10000000),
'IsPaused': paused,
'VolumeLevel': volume,
'IsMuted': muted,
'PlaySessionId': playsession_id
}
if playMethod == "Transcode":
# Track can't be changed, keep reporting the same index
postdata['AudioStreamIndex'] = audioindex
postdata['AudioStreamIndex'] = subtitleindex
else:
# Get current audio and subtitles track
params = {
'playerid': 1,
'properties': ["currentsubtitle","currentaudiostream","subtitleenabled"]
}
result = JSONRPC('Player.GetProperties').execute(params)
result = result.get('result')
try: # Audio tracks
indexAudio = result['currentaudiostream']['index']
except (KeyError, TypeError):
indexAudio = 0
try: # Subtitles tracks
indexSubs = result['currentsubtitle']['index']
except (KeyError, TypeError):
indexSubs = 0
try: # If subtitles are enabled
subsEnabled = result['subtitleenabled']
except (KeyError, TypeError):
subsEnabled = ""
# Postdata for the audio
data['AudioStreamIndex'], postdata['AudioStreamIndex'] = [indexAudio + 1] * 2
# Postdata for the subtitles
if subsEnabled and len(xbmc.Player().getAvailableSubtitleStreams()) > 0:
# Number of audiotracks to help get Emby Index
audioTracks = len(xbmc.Player().getAvailableAudioStreams())
mapping = window("emby_%s.indexMapping" % currentFile)
if mapping: # Set in PlaybackUtils.py
log.debug("Mapping for external subtitles index: %s" % mapping)
externalIndex = json.loads(mapping)
if externalIndex.get(str(indexSubs)):
# If the current subtitle is in the mapping
subindex = [externalIndex[str(indexSubs)]] * 2
data['SubtitleStreamIndex'], postdata['SubtitleStreamIndex'] = subindex
else:
# Internal subtitle currently selected
subindex = [indexSubs - len(externalIndex) + audioTracks + 1] * 2
data['SubtitleStreamIndex'], postdata['SubtitleStreamIndex'] = subindex
else: # Direct paths enabled scenario or no external subtitles set
subindex = [indexSubs + audioTracks + 1] * 2
data['SubtitleStreamIndex'], postdata['SubtitleStreamIndex'] = subindex
else:
data['SubtitleStreamIndex'], postdata['SubtitleStreamIndex'] = [""] * 2
# Report progress via websocketclient
log.debug("Report: %s", postdata)
self.emby.progress_report(postdata)
@log_error()
def onPlayBackSeek(self, time, seekOffset):
# Make position when seeking a bit more accurate
currentFile = self.currentFile
log.debug("PLAYBACK_SEEK: %s" % currentFile)
if self.played_info.get(currentFile):
position = None
try:
position = self.xbmcplayer.getTime()
except:
pass
if position is not None:
self.played_info[currentFile]['currentPosition'] = position
self.reportPlayback()
def stopAll(self):
if not self.played_info:
return
log.info("Played_information: %s" % self.played_info)
# Process each items
for item in self.played_info:
data = self.played_info.get(item)
if data:
log.debug("Item path: %s" % item)
log.debug("Item data: %s" % data)
runtime = data['runtime']
currentPosition = data['currentPosition']
itemid = data['item_id']
refresh_id = data['refresh_id']
currentFile = data['currentfile']
media_type = data['Type']
playMethod = data['playmethod']
self.stop_playback(data)
if currentPosition and runtime:
try:
if window('emby.external'):
window('emby.external', clear=True)
raise ValueError
percentComplete = (currentPosition * 10000000) / int(runtime)
except ZeroDivisionError:
# Runtime is 0.
percentComplete = 0
except ValueError:
percentComplete = 100
markPlayedAt = float(settings('markPlayed')) / 100
log.info("Percent complete: %s Mark played at: %s"
% (percentComplete, markPlayedAt))
# Send the delete action to the server.
offerDelete = False
if media_type == "Episode" and settings('deleteTV') == "true":
offerDelete = True
elif media_type == "Movie" and settings('deleteMovies') == "true":
offerDelete = True
if settings('offerDelete') != "true":
# Delete could be disabled, even if the subsetting is enabled.
offerDelete = False
if percentComplete >= markPlayedAt and offerDelete:
resp = xbmcgui.Dialog().yesno(lang(30091), lang(33015), autoclose=120000)
if resp:
url = "{server}/emby/Items/%s?format=json" % itemid
log.info("Deleting request: %s" % itemid)
self.doUtils(url, action_type="DELETE")
else:
log.info("User skipped deletion.")
window('emby.external_check', clear=True)
##### Track end of playlist
if media_type == "Audio":
playlist = xbmc.PlayList(xbmc.PLAYLIST_MUSIC)
else:
playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO)
if int(playlist.getposition()) < 0:
''' If playback is stopped within the first 30 seconds,
Kodi doesn't consider this watched and playlist should not be cleared.
When the end is reached, position is -1.
'''
log.info("Clear playlist, end detected.")
playlist.clear()
path = xbmc.translatePath(
"special://profile/addon_data/plugin.video.emby/temp/").decode('utf-8')
dirs, files = xbmcvfs.listdir(path)
for file in files:
xbmcvfs.delete("%s%s" % (path, file))
self.played_info.clear()
ga = GoogleAnalytics()
ga.sendEventData("PlayAction", "Stopped")
def stop_playback(self, data):
log.info("stop playback called.")
position_ticks = int(data['currentPosition'] * 10000000)
position = data['runtime'] if position_ticks and window('emby.external') else position_ticks
self.emby.stop_playback(data['item_id'], position, data['playsession_id'], data.get('mediasource_id'))
# Stop transcode
if data['playmethod'] == "Transcode":
log.info("Transcoding for %s terminated." % data['item_id'])
url = "{server}/emby/Videos/ActiveEncodings?DeviceId=%s" % self.clientInfo.get_device_id()
self.doUtils(url, action_type="DELETE")
"""

View file

@ -121,8 +121,7 @@ class Views(object):
def get_views(self):
''' Get all views and the media folders that make up the views.
Add custom views that are not media folders but should still be added
''' Get the media folders. Add or remove them.
'''
media = {
'movies': "Movie",

View file

@ -31,7 +31,7 @@ DELAY = int(settings('startupDelay') or 0)
if __name__ == "__main__":
LOG.info("--->[ service ]")
LOG.warn("--->[ service ]")
LOG.warn("Delay startup by %s seconds.", DELAY)
session = Service()
@ -46,4 +46,4 @@ if __name__ == "__main__":
LOG.exception(error)
session.shutdown()
LOG.info("---<[ service ]")
LOG.warn("---<[ service ]")