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
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')]
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'), ]
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)]
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
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())
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.")
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
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())
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')]
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')]
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
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
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
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