예제 #1
0
    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)
예제 #2
0
파일: engine.py 프로젝트: Zerokami/trackma
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)
예제 #3
0
파일: data.py 프로젝트: kondra/trackma
                    # 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.

        """
예제 #4
0
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)