def start(self): """ Does all necessary tasks to start the data handler This should be called before doing any other operation with the data handler, as it loads the list cache (or downloads it if necessary) and queue. """ # Lock the database self.msg.debug(self.name, "Locking database...") self._lock() if self._meta_exists(): self._load_meta() if self._queue_exists(): self._load_queue() if self._failed_searches_exists(): self._load_failed_searches() # If cache exists, load from it # otherwise query the API for a remote list if not self._cache_exists(): try: self.download_data() except utils.APIError, e: raise utils.APIFatal(e.message)
class Engine: """ The engine is the controller that handles commands coming from the user interface and then queries the Data Handler for the necessary data. It doesn't control nor care about how the data is fetched by the Data Handler. After instantiating this class, the :func:`start` must be run to initialize the engine. Likewise, the :func:`unload` function must be called when you're done using the engine. The account and mediatype can be changed later on the fly by calling :func:`reload`. The **account** parameter is an account dictionary passed by an Account Manager and is used to run the engine. The **message_handler** is a reference to a messaging function for the engine to send to. Optional. """ data_handler = None tracker = None config = dict() msg = None loaded = False playing = False hooks_available = False name = 'Engine' signals = { 'show_added': None, 'show_deleted': None, 'episode_changed': None, 'score_changed': None, 'status_changed': None, 'show_synced': None, 'sync_complete': None, 'queue_changed': None, 'playing': None, 'prompt_for_update': None, } def __init__(self, account, message_handler=None): """Reads configuration file and asks the data handler for the API info.""" self.msg = messenger.Messenger(message_handler) self._load(account) self._init_data_handler() def _load(self, account): self.account = account # Create home directory utils.make_dir('') self.configfile = utils.get_root_filename('config.json') # Create user directory userfolder = "%s.%s" % (account['username'], account['api']) utils.make_dir(userfolder) self.msg.info( self.name, 'Trackma v{0} - using account {1}({2}).'.format( utils.VERSION, account['username'], account['api'])) self.msg.info(self.name, 'Reading config files...') try: self.config = utils.parse_config(self.configfile, utils.config_defaults) except IOError: raise utils.EngineFatal("Couldn't open config file.") # Load hook file if os.path.exists(utils.get_root_filename('hook.py')): import sys sys.path[0:0] = [utils.get_root()] try: self.msg.info(self.name, "Importing user hooks (hook.py)...") global hook import hook self.hooks_available = True except ImportError: self.msg.warn(self.name, "Error importing hooks.") del sys.path[0] def _init_data_handler(self, mediatype=None): # Create data handler self.data_handler = data.Data(self.msg, self.config, self.account, mediatype) self.data_handler.connect_signal('show_synced', self._data_show_synced) self.data_handler.connect_signal('sync_complete', self._data_sync_complete) self.data_handler.connect_signal('queue_changed', self._data_queue_changed) # Record the API details (self.api_info, self.mediainfo) = self.data_handler.get_api_info() def _data_show_synced(self, show, changes): self._emit_signal('show_synced', show, changes) def _data_sync_complete(self, items): self._emit_signal('sync_complete', items) def _data_queue_changed(self, queue): self._emit_signal('queue_changed', queue) def _tracker_playing(self, showid, playing, episode): show = self.get_show_info(showid) self._emit_signal('playing', show, playing, episode) def _tracker_update(self, showid, episode): show = self.get_show_info(showid) if self.config['tracker_update_prompt']: self._emit_signal('prompt_for_update', show, episode) else: self.set_episode(show['id'], episode) def _emit_signal(self, signal, *args): try: if self.signals[signal]: self.signals[signal](*args) if self.hooks_available: method = getattr(hook, signal) self.msg.debug(self.name, "Calling hook %s..." % signal) method(self, *args) except AttributeError: pass def _get_tracker_list(self, filter_num=None): tracker_list = [] if filter_num: source_list = self.filter_list(filter_num) else: source_list = self.get_list() for show in source_list: tracker_list.append({ 'id': show['id'], 'title': show['title'], 'my_progress': show['my_progress'], 'type': None, 'titles': self.data_handler.get_show_titles(show), }) # TODO types return tracker_list def _update_tracker(self): if self.tracker: self.tracker.update_list(self._get_tracker_list()) def _cleanup(self): # If the engine wasn't closed for whatever reason, do it if self.loaded: self.msg.info(self.name, "Forcing exit...") self.data_handler.unload(True) self.loaded = False def connect_signal(self, signal, callback): try: self.signals[signal] = callback except KeyError: raise utils.EngineFatal("Invalid signal.") def set_message_handler(self, message_handler): """Changes the message handler function on the fly.""" self.msg = messenger.Messenger(message_handler) self.data_handler.set_message_handler(self.msg) def start(self): """ Starts the engine. This function should be called before doing anything with the engine, as it initializes the data handler. """ if self.loaded: raise utils.TrackmaError("Already loaded.") # Start the data handler try: (self.api_info, self.mediainfo) = self.data_handler.start() except utils.DataError, e: raise utils.DataFatal(e.message) except utils.APIError, e: raise utils.APIFatal(e.message)
# Make sure we process the queue first before overwriting the list # We don't want users losing their changes self.process_queue() self.download_data() except utils.APIError, e: self.msg.warn(self.name, "Couldn't download list! Using cache.") self._load_cache() elif not self.showlist: # If the cache wasn't loaded before, do it now self._load_cache() else: try: self.download_data() except utils.APIError, e: raise utils.APIFatal(e.message) # Create autosend thread if needed if self.config['autosend'] == 'hours': self.autosend() return (self.api.api_info, self.api.media_info()) def unload(self, force=False): """ Does unloading of the data handler This should be called whenever the data handler won't be used anymore, as it does necessary operations to close the API and the data handler itself. """
class Engine: """ Main engine class Controller that handles commands from the visual client and then queries the Data Handler for the necessary data. It doesn't care about how the data is fetched, it just handles the commands coming from the visual client. message_handler: Reference to a function for the engine to send to. Optional. """ data_handler = None config = dict() msg = None loaded = False playing = False last_show = None name = 'Engine' signals = { 'show_added': None, 'show_deleted': None, 'episode_changed': None, 'score_changed': None, 'status_changed': None, 'playing': None, } def __init__(self, account, message_handler=None): """Reads configuration file and asks the data handler for the API info.""" self.msg = messenger.Messenger(message_handler) self.msg.info(self.name, 'Version ' + VERSION) # Register cleanup function when program exits atexit.register(self._cleanup) self.load(account) self._init_data_handler() def load(self, account): self.account = account # Create home directory utils.make_dir('') self.configfile = utils.get_root_filename('config.json') # Create user directory userfolder = "%s.%s" % (account['username'], account['api']) utils.make_dir(userfolder) self.userconfigfile = utils.get_filename(userfolder, 'user.json') self.msg.info(self.name, 'Reading config files...') try: self.config = utils.parse_config(self.configfile, utils.config_defaults) self.userconfig = utils.parse_config(self.userconfigfile, utils.userconfig_defaults) except IOError: raise utils.EngineFatal("Couldn't open config file.") def _init_data_handler(self): # Create data handler self.data_handler = data.Data(self.msg, self.config, self.account, self.userconfig) # Record the API details (self.api_info, self.mediainfo) = self.data_handler.get_api_info() def _emit_signal(self, signal, args=None): try: if self.signals[signal]: self.signals[signal](args) except KeyError: raise Exception("Call to undefined signal.") def _cleanup(self): # If the engine wasn't closed for whatever reason, do it if self.loaded: self.unload() def connect_signal(self, signal, callback): try: self.signals[signal] = callback except KeyError: raise utils.EngineFatal("Invalid signal.") def set_message_handler(self, message_handler): """Changes the data handler even after the class initialization.""" self.msg = messenger.Messenger(message_handler) self.data_handler.set_message_handler(self.msg) def start(self): """ Starts the engine This function should be called before doing anything with the engine, as it initializes the data handler. """ if self.loaded: raise utils.wmalError("Already loaded.") # Start the data handler try: (self.api_info, self.mediainfo) = self.data_handler.start() except utils.DataError, e: raise utils.DataFatal(e.message) except utils.APIError, e: raise utils.APIFatal(e.message)