Skip to content

Commit

Permalink
Merge pull request #127 from wackerl91/feature/LUNA-67
Browse files Browse the repository at this point in the history
LUNA-67: Eos integration
  • Loading branch information
wackerl91 committed Feb 12, 2017
2 parents 8eaf67e + e8466cb commit 8a03715
Show file tree
Hide file tree
Showing 27 changed files with 572 additions and 39 deletions.
2 changes: 1 addition & 1 deletion addon.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<addon id="script.luna" name="Luna" version="0.7.0~rc4" provider-name="wackerl91">
<addon id="script.luna" name="Luna" version="0.7.0~rc6" provider-name="wackerl91">
<requires>
<import addon="xbmc.python" version="2.20.0"/>
<import addon="script.module.pyxbmct" version="1.1.4" />
Expand Down
1 change: 1 addition & 0 deletions resources/language/English/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
<string id="30041">Enable pre / post scripts</string>
<string id="30042">Pre Script</string>
<string id="30043">Post Script</string>
<string id="30044">Enable Telemetry Data via Eos</string>
<!-- Context Menu -->
<string id="30100">Addon Settings</string>
<string id="30101">Full Refresh</string>
Expand Down
30 changes: 30 additions & 0 deletions resources/lib/config/features.yml
Original file line number Diff line number Diff line change
Expand Up @@ -344,3 +344,33 @@ services:
- '@scraper-chain'
tags:
- { name: controller }

eos-helper:
module: resources.lib.eos.eoshelper
class_name: EosHelper
arguments:
- '@core'
- '@host-manager'
- '@game-manager'

#
# Loggers
#

default-logger:
module: resources.lib.core.logger.logger
class_name: Logger
arguments:
- 'debug'
tags:
- { name: logger-chain }

eos-logger:
module: resources.lib.core.logger.eoslogger
class_name: EosLogger
arguments:
- 'warning'
tags:
- { name: logger-chain }
calls:
- [ set_helper, ['@eos-helper'] ]
15 changes: 15 additions & 0 deletions resources/lib/core/corefunctions.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import json
import os
import stat

Expand Down Expand Up @@ -38,6 +39,7 @@ class Core:
def __init__(self, logger):
self.addon = xbmcaddon.Addon()
self.logger = logger
self.kodi_version = None
self._storage_path = xbmc.translatePath(
'special://profile/addon_data/%s/.storage/' % self.addon.getAddonInfo('id'))
self._current_version = re.match(self.regexp, self.addon.getAddonInfo('version')).group()
Expand Down Expand Up @@ -193,6 +195,19 @@ def _create_path(self):

os.makedirs(self._storage_path)

def get_kodi_version(self):
if self.kodi_version is None:
response = xbmc.executeJSONRPC(
'{ "jsonrpc": "2.0", "method": "Application.GetProperties", "params": {"properties": ["version", "name"]}, "id": 1 }')
self.logger.info(response)
response = unicode(response, 'utf-8')
response = json.loads(response)

if 'result' in response and 'version' in response['result']:
self.kodi_version = response['result']['version']

return self.kodi_version

@property
def current_version(self):
return self._current_version
Expand Down
25 changes: 0 additions & 25 deletions resources/lib/core/logger.py

This file was deleted.

Empty file.
38 changes: 38 additions & 0 deletions resources/lib/core/logger/abstractlogger.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
from abc import ABCMeta, abstractmethod


class AbstractLogger:
__metaclass__ = ABCMeta

LEVELS = {
'debug': 0,
'info': 1,
'notice': 2,
'warning': 3,
'error': 4,
'severe': 5,
'fatal': 6
}

def __init__(self, log_level):
self.log_level = log_level

@abstractmethod
def debug(self, channel, text):
pass

@abstractmethod
def info(self, channel, text):
pass

@abstractmethod
def warning(self, channel, text):
pass

@abstractmethod
def error(self, channel, text):
pass

@abstractmethod
def critical(self, channel, text):
pass
29 changes: 29 additions & 0 deletions resources/lib/core/logger/eoslogger.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
from resources.lib.core.logger.abstractlogger import AbstractLogger


class EosLogger(AbstractLogger):
def __init__(self, log_level):
super(EosLogger, self).__init__(log_level)
self.eos_helper = None

def warning(self, channel, text):
self._log('warning', channel, text)

def error(self, channel, text):
self._log('error', channel, text)

def debug(self, channel, text):
self._log('debug', channel, text)

def info(self, channel, text):
self._log('info', channel, text)

def critical(self, channel, text):
self._log('critical', channel, text)

def set_helper(self, eos_helper):
self.eos_helper = eos_helper

def _log(self, level, channel, text):
if self.eos_helper is not None and self.LEVELS[level] >= self.LEVELS[self.log_level]:
self.eos_helper.log(level, channel, text)
24 changes: 24 additions & 0 deletions resources/lib/core/logger/logger.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import xbmc

from resources.lib.core.logger.abstractlogger import AbstractLogger


class Logger(AbstractLogger):
def debug(self, channel, text):
xbmc.log(self._format(channel, text), xbmc.LOGDEBUG)

def info(self, channel, text):
xbmc.log(self._format(channel, text), xbmc.LOGNOTICE)

def warning(self, channel, text):
xbmc.log(self._format(channel, text), xbmc.LOGWARNING)

def error(self, channel, text):
xbmc.log(self._format(channel, text), xbmc.LOGERROR)

def critical(self, channel, text):
xbmc.log(self._format(channel, text), xbmc.LOGSEVERE)

def _format(self, channel, text):
text = str(text)
return '[%s]: %s' % (channel, text)
37 changes: 37 additions & 0 deletions resources/lib/core/logger/loggerchain.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
from resources.lib.core.logger.abstractlogger import AbstractLogger


class LoggerChain(object):
def __init__(self, prefix):
self.prefix = prefix
self.logger_chain = []

def debug(self, message):
for logger in self.logger_chain:
logger.debug(self.prefix, message)

def info(self, message):
for logger in self.logger_chain:
logger.info(self.prefix, message)

def warning(self, message):
for logger in self.logger_chain:
logger.warning(self.prefix, message)

def error(self, message):
for logger in self.logger_chain:
logger.error(self.prefix, message)

def critical(self, message):
for logger in self.logger_chain:
logger.critical(self.prefix, message)

def append(self, loggers):
for logger in loggers:
self._append_logger(logger)

def _append_logger(self, logger):
if isinstance(logger, AbstractLogger):
self.logger_chain.append(logger)
else:
raise AssertionError('Expected to receive an instance of AbstractLogger, got %s instead' % type(logger))
7 changes: 4 additions & 3 deletions resources/lib/di/component.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ class Component(yaml.YAMLObject):
"""Symbolic base class for components"""
yaml_tag = u'!component'

def __init__(self, name, module, class_name, arguments, tags, factory_class, factory_method, lazy):
def __init__(self, name, module, class_name, arguments, tags, factory_class, factory_method, lazy, calls):
self.name = name
self.module = module
self.class_name = class_name
Expand All @@ -14,8 +14,9 @@ def __init__(self, name, module, class_name, arguments, tags, factory_class, fac
self.factory_class = factory_class
self.factory_method = factory_method
self.lazy = lazy
self.calls = calls

@classmethod
def from_dict(cls, name, module=None, class_name=None, arguments=None, tags=None, factory_class=None,
factory_method=None, lazy=None, **kwargs):
return cls(name, module, class_name, arguments, tags, factory_class, factory_method, lazy)
factory_method=None, lazy=None, calls=None, **kwargs):
return cls(name, module, class_name, arguments, tags, factory_class, factory_method, lazy, calls)
53 changes: 50 additions & 3 deletions resources/lib/di/featurebroker.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,8 @@ def tag(self, tag, feature):
if tag not in self.tags[tag.name]:
self.tags[tag.name].append(tag)

self.tagged_features[tag.name].append(feature)
if feature not in self.tagged_features[tag.name]:
self.tagged_features[tag.name].append(feature)

def get_tagged_features(self, tag_name):
try:
Expand All @@ -84,8 +85,8 @@ def get_tagged_features(self, tag_name):
return tagged_features

def _provide_loggers(self):
module = importlib.import_module('resources.lib.core.logger')
class_name = 'Logger'
module = importlib.import_module('resources.lib.core.logger.loggerchain')
class_name = 'LoggerChain'
class_ = getattr(module, class_name)

addon_id = xbmcaddon.Addon().getAddonInfo('id')
Expand All @@ -94,6 +95,24 @@ def _provide_loggers(self):
logger_prefix = '%s.%s' % (addon_id, logger_definition.channel)
_logger = class_(logger_prefix)

logger_services = list(self.tagged_features['logger-chain'])

del_keys = []

for key, logger_service in enumerate(logger_services):
from resources.lib.di.requiredfeature import RequiredFeature
for tag in logger_service.tags:
if tag.name == 'logger-chain':
if hasattr(tag, 'ignore_channel') and tag.ignore_channel == logger_definition.channel:
del_keys.append(key)
else:
logger_services[key] = RequiredFeature(logger_service.name).request()

for key in del_keys:
del logger_services[key]

_logger.append(logger_services)

_logger_service_name = 'logger.%s' % logger_definition.channel

self.set_initialized(_logger_service_name, _logger)
Expand Down Expand Up @@ -127,6 +146,34 @@ def set_initialized(self, feature, instance):
assert feature not in self.initialized, "Duplicate instance: %s" % feature
self.initialized[feature] = instance

def execute_calls(self):
for feature_name, feature in self.providers.iteritems():
if not isinstance(feature, Component):
continue
instance = self.get_initialized(feature.name)
if instance and hasattr(feature, 'calls') and feature.calls is not None:
for call in feature.calls:
method = call[0]
args = call[1]

for key, arg in enumerate(args):
if arg[:1] == '@':
if self.get_initialized(arg[1:]) is not None:
args[key] = self.get_initialized(arg[1:])
else:
from resources.lib.di.requiredfeature import RequiredFeature
args[key] = RequiredFeature(arg[1:]).request()

resolved_all_services = True
for key, arg in enumerate(args):
if isinstance(arg, str) and arg[:1] == '@':
resolved_all_services = False

if resolved_all_services:
if hasattr(instance, method):
method_ = getattr(instance, method)
method_(*args)

def __getitem__(self, feature):
try:
provider = self.providers[feature]
Expand Down
22 changes: 21 additions & 1 deletion resources/lib/di/requiredfeature.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ def _build_attributes_dict(self, class_, feature):
args = getattr(feature, 'arguments', None)
if args is not None:
for index, arg in enumerate(feature.arguments):
if arg[:1] == '@':
# TODO: checking for instance is generally a bad idea and only a temporary workaround to allow
# eos-helper's functionality
if isinstance(arg, str) and arg[:1] == '@':
feature.arguments[index] = RequiredFeature(arg[1:]).request()
args = inspect.getargspec(class_.__init__)[0]
if args[0] == 'self':
Expand Down Expand Up @@ -90,4 +92,22 @@ def request(self):

featurebroker.features.set_initialized(self.feature, instance)

if hasattr(feature, 'calls') and feature.calls is not None:
for call in feature.calls:
method = call[0]
args = call[1]

for key, arg in enumerate(args):
if arg[:1] == '@':
if featurebroker.features.get_initialized(arg[:1]) is not None:
args[key] = featurebroker.features.get_initialized(arg[:1])

resolved_all_services = True
for key, arg in enumerate(args):
if isinstance(arg, str) and arg[:1] == '@':
resolved_all_services = False

if resolved_all_services:
method(instance, **args)

return instance
Empty file added resources/lib/eos/__init__.py
Empty file.
Loading

0 comments on commit 8a03715

Please sign in to comment.