示例#1
0
    def search(self, criteria, method):
        self.check_credentials()

        results = list()
        self.msg.info(self.name, 'Searching for %s...' % criteria)

        (name, data) = self._sendcmd(
            'get vn basic,details (search ~ "%s")' % criteria, {
                'page': 1,
                'results': self.pagesize_details,
            })

        # Something is wrong if we don't get a results response.
        if name != 'results':
            raise utils.APIFatal("Invalid response (%s)" % name)

        # Process list
        for item in data['items']:
            results.append(self._parse_info(item))

        self._emit_signal('show_info_changed', results)

        if not results:
            raise utils.APIError('No results.')

        return results
示例#2
0
    def __init__(self, messenger, account, userconfig):
        """Initializes the API"""
        super(libanilist, self).__init__(messenger, account, userconfig)

        self.pin = account['password'].strip()
        self.userid = userconfig['userid']

        if len(self.pin) != 40:
            raise utils.APIFatal("Invalid PIN.")

        if self.mediatype == 'manga':
            self.total_str = "total_chapters"
            self.watched_str = "chapters_read"
            self.airing_str = "publishing_status"
            self.status_translate = {
                'publishing': utils.STATUS_AIRING,
                'finished publishing': utils.STATUS_FINISHED,
                'not yet published': utils.STATUS_NOTYET,
                'cancelled': utils.STATUS_CANCELLED,
            }
        else:
            self.total_str = "total_episodes"
            self.watched_str = "episodes_watched"
            self.airing_str = "airing_status"
            self.status_translate = {
                'currently airing': utils.STATUS_AIRING,
                'finished airing': utils.STATUS_FINISHED,
                'not yet aired': utils.STATUS_NOTYET,
                'cancelled': utils.STATUS_CANCELLED,
            }

        #handler=urllib.request.HTTPHandler(debuglevel=1)
        #self.opener = urllib.request.build_opener(handler)
        self.opener = urllib.request.build_opener()
        self.opener.addheaders = [('User-agent', 'Trackma/0.1')]
示例#3
0
    def __init__(self, messenger, account, userconfig):
        super(libmal, self).__init__(messenger, account, userconfig)

        self.pin = account['password'].strip()

        if 'extra' not in account or 'code_verifier' not in account['extra']:
            raise utils.APIFatal(
                "This account seems to be using the old MyAnimeList API."
                "Please re-create and authorize the account.")

        self.code_verifier = account['extra']['code_verifier']
        self.userid = self._get_userconfig('userid')

        if self.mediatype == 'manga':
            self.total_str = "num_chapters"
            self.watched_str = self.watched_send_str = "num_chapters_read"
        else:
            self.total_str = "num_episodes"
            self.watched_str = "num_episodes_watched"
            self.watched_send_str = "num_watched_episodes"  # Please fix this upstream...

        self.opener = urllib.request.build_opener()
        self.opener.addheaders = [
            ('User-Agent', self.user_agent),
            ('Accept', 'application/json'),
            ('Accept-Encoding', 'gzip'),
            ('Accept-Charset', 'utf-8'),
        ]
示例#4
0
    def __init__(self, messenger, account, userconfig):
        """Initializes the API"""
        super(libanilist, self).__init__(messenger, account, userconfig)

        self.pin = account['password'].strip()
        self.userid = self._get_userconfig('userid')

        if len(
                self.pin
        ) == 40:  # Old pins were 40 digits, new ones seem to be 654 digits
            raise utils.APIFatal(
                "This appears to be a V1 API PIN. You need a V2 API PIN to continue using Anilist."
            )
        #elif len(self.pin) != 654:
        #    raise utils.APIFatal("Invalid PIN.")

        if self.mediatype == 'manga':
            self.total_str = "chapters"
            self.watched_str = "chapters_read"
        else:
            self.total_str = "episodes"
            self.watched_str = "episodes_watched"
        self.status_translate = {
            'RELEASING': utils.STATUS_AIRING,
            'FINISHED': utils.STATUS_FINISHED,
            'NOT_YET_RELEASED': utils.STATUS_NOTYET,
            'CANCELLED': utils.STATUS_CANCELLED,
        }

        self.opener = urllib.request.build_opener()
        self.opener.addheaders = [('User-agent', self.user_agent)]
示例#5
0
    def request_info(self, itemlist):
        self.check_credentials()

        start = 0
        infos = list()
        remaining = [show['id'] for show in itemlist]
        while True:
            self.msg.info(self.name, 'Requesting details...(%d)' % start)
            end = start + self.pagesize_details

            (name, data) = self._sendcmd(
                'get vn basic,details (id = %s)' % repr(remaining[start:end]),
                {
                    'page': 1,
                    'results': self.pagesize_details,
                })

            # Something is wrong if we don't get a results response.
            if name != 'results':
                raise utils.APIFatal("Invalid response (%s)" % name)

            # Process list
            for item in data['items']:
                infos.append(self._parse_info(item))

            start += self.pagesize_details
            if start >= len(itemlist):
                # We're going beyond the list, finish
                break

        self._emit_signal('show_info_changed', infos)
        return infos
示例#6
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()

        # Load different caches
        if self._meta_exists():
            self._load_meta()

        if self._queue_exists() and self.meta.get('version') == self.version and self.meta.get('apiversion') == self.api_version:
            self._load_queue()
            self._emit_signal('queue_changed', self.queue)

        if self._info_exists() and self.meta.get('version') == self.version and self.meta.get('apiversion') == self.api_version:
            # Load info cache only if we're on the same database version
            self._load_info()

        # If there is a list cache, load from it
        # otherwise query the API for a remote list
        if self._cache_exists() and self.meta.get('version') == self.version and self.meta.get('apiversion') == self.api_version:
            # Auto-send: Process the queue if we're beyond the auto-send time limit for some reason
            if self._is_queue_ready():
                self.process_queue()

            # Auto-retrieve: Redownload list if any autoretrieve condition is met
            if (self.config['autoretrieve'] == 'always' or
                (self.config['autoretrieve'] == 'days' and
                 time.time() - self.meta['lastget'] > self.config['autoretrieve_days'] * 84600) or
                    self.meta.get('version') != self.version):
                try:
                    # 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 as 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 as e:
                raise utils.APIFatal(str(e))

        # Create autosend thread if needed
        # Note: Hours setting is DEPRECATED!
        if self.config['autosend'] in ('minutes', 'hours'):
            self.autosend()

        return (self.api.api_info, self.api.media_info())
示例#7
0
    def fetch_list(self):
        """Queries the full list from the remote server.
        Returns the list if successful, False otherwise."""
        self.check_credentials()
        self.msg.info(self.name, 'Downloading list...')

        try:
            # Get an XML list from MyAnimeList API
            data = self._request("http://myanimelist.net/malappinfo.php?u=" +
                                 self.username + "&status=all&type=" +
                                 self.mediatype)

            # Parse the XML data and load it into a dictionary
            # using the proper function (anime or manga)
            root = ET.ElementTree().parse(data, parser=self._make_parser())

            if self.mediatype == 'anime':
                self.msg.info(self.name, 'Parsing anime list...')
                return self._parse_anime(root)
            elif self.mediatype == 'manga':
                self.msg.info(self.name, 'Parsing manga list...')
                return self._parse_manga(root)
            else:
                raise utils.APIFatal(
                    'Attempted to parse unsupported media type.')
        except urllib2.HTTPError, e:
            raise utils.APIError("Error getting list.")
示例#8
0
    def check_credentials(self):
        if len(self.pin) == 40:  # Old pins were 40 digits, new ones seem to be 654 digits
            raise utils.APIFatal("This appears to be a V1 API PIN. You need a V2 API PIN to continue using AniList."
                                 " Please re-authorize or re-create your AniList account.")

        if not self.userid:
            self._refresh_user_info()

        return True
示例#9
0
    def __init__(self, messenger, account, userconfig):
        """Initializes the API"""
        self.userconfig = userconfig
        self.msg = messenger
        self.msg.info(self.name, 'Initializing...')

        if not userconfig.get('mediatype'):
            userconfig['mediatype'] = self.default_mediatype

        if userconfig['mediatype'] in self.mediatypes:
            self.mediatype = userconfig['mediatype']
        else:
            raise utils.APIFatal('Unsupported mediatype %s.' % userconfig['mediatype'])

        self.api_info['mediatype'] = self.mediatype
        self.api_info['supported_mediatypes'] = list(self.mediatypes.keys())
示例#10
0
    def __init__(self, messenger, account, userconfig):
        """Initializes the API"""
        super(libshikimori, self).__init__(messenger, account, userconfig)

        self.pin = account['password']
        self.userid = self._get_userconfig('userid')

        if not self.pin:
            raise utils.APIFatal("No PIN.")

        if self.mediatype == 'manga':
            self.total_str = "chapters"
            self.watched_str = "chapters"

        else:
            self.total_str = "episodes"
            self.watched_str = "episodes"

        # handler=urllib.request.HTTPHandler(debuglevel=1)
        #self.opener = urllib.request.build_opener(handler)
        self.opener = urllib.request.build_opener()
        self.opener.addheaders = [('User-agent', 'Trackma')]
示例#11
0
    def __init__(self, messenger, account, userconfig):
        """Initializes the API"""
        super(libshikimori, self).__init__(messenger, account, userconfig)

        self.username = account['username']
        self.password = account['password']
        self.userid = userconfig['userid']

        if not self.password:
            raise utils.APIFatal("No password.")

        if self.mediatype == 'manga':
            self.total_str = "chapters"
            self.watched_str = "chapters"
            self.airing_str = "publishing_status"
            self.status_translate = {
                'publishing': utils.STATUS_AIRING,
                'finished': utils.STATUS_FINISHED,
                'not yet published': utils.STATUS_NOTYET,
                'cancelled': utils.STATUS_CANCELLED,
            }
        else:
            self.total_str = "episodes"
            self.watched_str = "episodes"
            self.airing_str = "airing_status"
            self.status_translate = {
                'currently airing': utils.STATUS_AIRING,
                'finished airing': utils.STATUS_FINISHED,
                'not yet aired': utils.STATUS_NOTYET,
                'cancelled': utils.STATUS_CANCELLED,
            }

        #handler=urllib2.HTTPHandler(debuglevel=1)
        #self.opener = urllib2.build_opener(handler)
        self.opener = urllib2.build_opener()
        self.opener.addheaders = [('User-agent', 'Trackma/0.4')]
示例#12
0
    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 as e:
            raise utils.DataFatal(str(e))
        except utils.APIError as e:
            raise utils.APIFatal(str(e))

        # Load redirection file if supported
        api = self.api_info['shortname']
        mediatype = self.data_handler.userconfig['mediatype']
        if redirections.supports(api, mediatype):
            if utils.file_exists(utils.to_config_path('anime-relations.txt')):
                fname = utils.to_config_path('anime-relations.txt')
                self.msg.debug(self.name, "Using user-provided redirection file.")
            else:
                fname = utils.to_data_path('anime-relations.txt')
                if self.config['redirections_time'] and (
                        not utils.file_exists(fname) or
                        utils.file_older_than(fname, self.config['redirections_time'] * 86400)):
                    self.msg.info(self.name, "Syncing redirection file...")
                    self.msg.debug(self.name, "Syncing from: %s" % self.config['redirections_url'])
                    utils.sync_file(fname, self.config['redirections_url'])

            if not utils.file_exists(fname):
                self.msg.debug(self.name, "Defaulting to repo provided redirections file.")
                fname = utils.DATADIR + '/anime-relations/anime-relations.txt'

            self.msg.info(self.name, "Parsing redirection file...")
            try:
                self.redirections = redirections.parse_anime_relations(fname, api)
            except Exception as e:
                self.msg.warn(self.name, "Error parsing anime-relations.txt!")
                self.msg.debug(self.name, "{}".format(e))

        # Rescan library if necessary
        if self.config['library_autoscan']:
            try:
                self.scan_library()
            except utils.TrackmaError as e:
                self.msg.warn(
                    self.name, "Can't auto-scan library: {}".format(e))

        # Load hook files
        if self.config['use_hooks']:
            hooks_dir = utils.to_config_path('hooks')
            if os.path.isdir(hooks_dir):
                import pkgutil

                self.msg.info(self.name, "Importing user hooks...")
                for loader, name, ispkg in pkgutil.iter_modules([hooks_dir]):
                    # List all the hook files in the hooks folder, import them
                    # and call the init() function if they have them
                    # We build the list "hooks available" with the loaded modules
                    # for later calls.
                    try:
                        self.msg.debug(
                            self.name, "Importing hook {}...".format(name))
                        module = loader.find_module(name).load_module(name)
                        if hasattr(module, 'init'):
                            module.init(self)
                        self.hooks_available.append(module)
                    except ImportError:
                        self.msg.warn(
                            self.name, "Error importing hook {}.".format(name))
                        import traceback
                        exc_type, exc_value, exc_traceback = sys.exc_info()
                        for line in traceback.format_exception(exc_type, exc_value, exc_traceback):
                            self.msg.debug(self.name, line.rstrip())

        # Start tracker
        if self.mediainfo.get('can_play') and self.config['tracker_enabled']:
            self.msg.debug(self.name, "Initializing tracker...")
            try:
                TrackerClass = self._get_tracker_class(
                    self.config['tracker_type'])

                self.tracker = TrackerClass(self.msg,
                                            self._get_tracker_list(),
                                            self.config,
                                            self.searchdirs,
                                            self.redirections,
                                            )
                self.tracker.connect_signal('detected', self._tracker_detected)
                self.tracker.connect_signal('removed', self._tracker_removed)
                self.tracker.connect_signal('playing', self._tracker_playing)
                self.tracker.connect_signal('update', self._tracker_update)
                self.tracker.connect_signal(
                    'unrecognised', self._tracker_unrecognised)
                self.tracker.connect_signal('state', self._tracker_state)
            except ImportError:
                self.msg.warn(self.name, "Couldn't import specified tracker: {}".format(
                    self.config['tracker_type']))

        self.loaded = True
        return True
示例#13
0
    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 as e:
            raise utils.DataFatal(str(e))
        except utils.APIError as e:
            raise utils.APIFatal(str(e))

        # Rescan library if necessary
        if self.config['library_autoscan']:
            try:
                self.scan_library()
            except utils.TrackmaError as e:
                self.msg.warn(self.name,
                              "Can't auto-scan library: {}".format(e))

        # Start tracker
        if self.mediainfo.get('can_play') and self.config['tracker_enabled']:
            # Choose the tracker we want to tart
            if self.config['tracker_type'] == 'plex':
                from trackma.tracker.plex import PlexTracker
                TrackerClass = PlexTracker
            elif os.name == 'nt':
                from trackma.tracker.win32 import Win32Tracker
                TrackerClass = Win32Tracker
            else:
                # Try trackers in this order: pyinotify, inotify, polling
                try:
                    from trackma.tracker.pyinotify import pyinotifyTracker
                    TrackerClass = pyinotifyTracker
                except ImportError:
                    try:
                        from trackma.tracker.inotify import inotifyTracker
                        TrackerClass = inotifyTracker
                    except ImportError:
                        from trackma.tracker.polling import PollingTracker
                        TrackerClass = PollingTracker

            self.tracker = TrackerClass(
                self.msg,
                self._get_tracker_list(),
                self.config['tracker_process'],
                self.config['searchdir'],
                int(self.config['tracker_interval']),
                int(self.config['tracker_update_wait_s']),
                self.config['tracker_update_close'],
                self.config['tracker_not_found_prompt'],
            )
            self.tracker.connect_signal('detected', self._tracker_detected)
            self.tracker.connect_signal('removed', self._tracker_removed)
            self.tracker.connect_signal('playing', self._tracker_playing)
            self.tracker.connect_signal('update', self._tracker_update)
            self.tracker.connect_signal('unrecognised',
                                        self._tracker_unrecognised)
            self.tracker.connect_signal('state', self._tracker_state)

        self.loaded = True
        return True
示例#14
0
    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 as e:
            raise utils.DataFatal(str(e))
        except utils.APIError as e:
            raise utils.APIFatal(str(e))

        # Rescan library if necessary
        if self.config['library_autoscan']:
            try:
                self.scan_library()
            except utils.TrackmaError as e:
                self.msg.warn(self.name,
                              "Can't auto-scan library: {}".format(e))

        # Load hook files
        if self.config['use_hooks']:
            hooks_dir = utils.to_config_path('hooks')
            if os.path.isdir(hooks_dir):
                import sys
                import pkgutil

                self.msg.info(self.name, "Importing user hooks...")
                for loader, name, ispkg in pkgutil.iter_modules([hooks_dir]):
                    # List all the hook files in the hooks folder, import them
                    # and call the init() function if they have them
                    # We build the list "hooks available" with the loaded modules
                    # for later calls.
                    try:
                        self.msg.debug(self.name,
                                       "Importing hook {}...".format(name))
                        module = loader.find_module(name).load_module(name)
                        if hasattr(module, 'init'):
                            module.init(self)
                        self.hooks_available.append(module)
                    except ImportError:
                        self.msg.warn(self.name,
                                      "Error importing hook {}.".format(name))

        # Start tracker
        if self.mediainfo.get('can_play') and self.config['tracker_enabled']:
            self.msg.debug(self.name, "Initializing tracker...")
            try:
                TrackerClass = self._get_tracker_class(
                    self.config['tracker_type'])

                self.tracker = TrackerClass(
                    self.msg,
                    self._get_tracker_list(),
                    self.config['tracker_process'],
                    self.searchdirs,
                    int(self.config['tracker_interval']),
                    int(self.config['tracker_update_wait_s']),
                    self.config['tracker_update_close'],
                    self.config['tracker_not_found_prompt'],
                )
                self.tracker.connect_signal('detected', self._tracker_detected)
                self.tracker.connect_signal('removed', self._tracker_removed)
                self.tracker.connect_signal('playing', self._tracker_playing)
                self.tracker.connect_signal('update', self._tracker_update)
                self.tracker.connect_signal('unrecognised',
                                            self._tracker_unrecognised)
                self.tracker.connect_signal('state', self._tracker_state)
            except ImportError:
                self.msg.warn(
                    self.name, "Couldn't import specified tracker: {}".format(
                        self.config['tracker_type']))

        self.loaded = True
        return True
示例#15
0
    def fetch_list(self):
        """Queries the full list from the remote server.
        Returns the list if successful, False otherwise."""
        self.check_credentials()

        # Retrieve VNs per pages
        page = 1
        vns = dict()
        while True:
            self.msg.info(self.name, 'Downloading list... (%d)' % page)

            (name,
             data) = self._sendcmd('get %s basic (uid = 0)' % self.mediatype, {
                 'page': page,
                 'results': self.pagesize_list
             })

            # Something is wrong if we don't get a results response.
            if name != 'results':
                raise utils.APIFatal("Invalid response (%s)" % name)

            # Process list
            for item in data['items']:
                vnid = item['vn']
                vns[vnid] = utils.show()
                vns[vnid]['id'] = vnid
                vns[vnid]['url'] = self._get_url(vnid)
                vns[vnid]['my_status'] = item.get('status',
                                                  item.get('priority'))

            if not data['more']:
                # No more VNs, finish
                break
            page += 1

        # Retrieve scores per pages
        page = 1
        while True:
            self.msg.info(self.name, 'Downloading votes... (%d)' % page)

            (name, data) = self._sendcmd('get votelist basic (uid = 0)', {
                'page': page,
                'results': self.pagesize_list
            })

            # Something is wrong if we don't get a results response.
            if name != 'results':
                raise utils.APIFatal("Invalid response (%s)" % name)

            for item in data['items']:
                vnid = item['vn']
                if vnid not in vns:
                    # Ghost vote; create entry for it.
                    vns[vnid] = utils.show()
                    vns[vnid]['id'] = vnid
                    vns[vnid]['url'] = self._get_url(vnid)
                    vns[vnid]['my_status'] = 0

                vns[vnid]['my_score'] = (item['vote'] / 10.0)
                vns[vnid]['my_finish_date'] = datetime.datetime.fromtimestamp(
                    item['added'])

            if not data['more']:
                # No more VNs, finish
                break
            page += 1

        return vns