diff --git a/updatorr/core.py b/updatorr/core.py index daf55b2..8f89979 100644 --- a/updatorr/core.py +++ b/updatorr/core.py @@ -17,6 +17,8 @@ # Line below is required to import tracker handler on fly. import updatorr.tracker_handlers from updatorr.utils import * +import sys +import traceback log = logging.getLogger(__name__) @@ -181,78 +183,81 @@ def walk(self, force=False): # To prevent possible concurent runs. self.walking = True - - log.info('Updatorr walking...') - component.get('EventManager').emit(UpdatorrUpdatesCheckStartedEvent()) - - allow_last_walk_update = False - - if isinstance(force, list): - torrents_list = force - else: - torrents_list = self.torrents_to_update - - for torrent_id in torrents_list: - try: - torrent_data = self.core.get_torrent_status(torrent_id, []) - except KeyError: - log.debug('Updatorr \tSKIPPED No torrent with id %s listed [yet]' % torrent_id) - continue - log.info('Updatorr Processing %s ...' % torrent_data['name']) - # Remove not url data from comment - torrent_data['comment'] = RE_LINK.search(torrent_data['comment']).group('url') - if not is_url(torrent_data['comment']): - log.info('Updatorr \tSKIPPED No URL found in torrent comment') - continue - # From now on we consider that update took its place. - # If only this update is not forced. - if not force: - allow_last_walk_update = True - tracker_handler = get_tracker_handler(torrent_data, log) - if tracker_handler is None: - self.dump_error(torrent_id, 'Unable to find tracker handler for %s' % torrent_data['comment']) - continue - tracker_handler.set_settings(self.trackers_settings.get(tracker_handler.tracker_host)) - new_torrent_filepath = tracker_handler.get_torrent_file() - if new_torrent_filepath is None: - self.dump_error(torrent_id, 'Error in tracker handling: %s' % tracker_handler.get_error_text()) - continue - - # Let's store cookies form that tracker to enter without logins in future sessions. - self.trackers_settings[tracker_handler.tracker_host]['cookies'] = tracker_handler.get_cookies(as_dict=True) - - new_torrent_contents = read_torrent_file(new_torrent_filepath) - new_torrent_info = read_torrent_info(new_torrent_contents) - if torrent_data['hash'] == new_torrent_info['hash']: - log.info('Updatorr \tSKIPPED Torrent is up-to-date') - continue - log.info('Updatorr \tTorrent update is available') - - new_torrent_prefs = get_new_prefs(torrent_data, new_torrent_info) - added_torrent_id = self.core.add_torrent_file(None, base64.encodestring(new_torrent_contents), new_torrent_prefs) - - if added_torrent_id is not None: - self.core.remove_torrent(torrent_id, False) - log.info('Updatorr \tTorrent is updated') - # Fire up update finished event. - component.get('EventManager').emit(UpdatorrUpdateDoneEvent(new_torrent_info['hash'])) - # Add new torrent hash to continue autoupdates. - self.set_items_to_update(new_torrent_info['hash'], True) - # Remove old torrent from autoupdates list. - self.set_items_to_update(torrent_id, False) + try: + log.info('Updatorr walking...') + component.get('EventManager').emit(UpdatorrUpdatesCheckStartedEvent()) + + allow_last_walk_update = False + + if isinstance(force, list): + torrents_list = force else: - self.dump_error(torrent_id, 'Unable to replace current torrent with a new one') - - # No littering, remove temporary .torrent file. - os.remove(new_torrent_filepath) - - if allow_last_walk_update: - # Remember lastrun time. - self.last_walk = time.time() - - log.info('Updatorr walk is finished') - component.get('EventManager').emit(UpdatorrUpdatesCheckFinishedEvent()) - self.walking = False + torrents_list = self.torrents_to_update + + for torrent_id in torrents_list: + try: + torrent_data = self.core.get_torrent_status(torrent_id, []) + log.info('Updatorr Processing %s ...' % torrent_data['name']) + except KeyError: + log.debug('Updatorr \tSKIPPED No torrent with id %s listed [yet]' % torrent_id) + continue + # Remove not url data from comment + torrent_data['comment'] = RE_LINK.search(torrent_data['comment']).group('url') + if not is_url(torrent_data['comment']): + log.info('Updatorr \tSKIPPED No URL found in torrent comment') + continue + # From now on we consider that update took its place. + # If only this update is not forced. + if not force: + allow_last_walk_update = True + tracker_handler = get_tracker_handler(torrent_data, log) + if tracker_handler is None: + self.dump_error(torrent_id, 'Unable to find tracker handler for %s' % torrent_data['comment']) + continue + tracker_handler.set_settings(self.trackers_settings.get(tracker_handler.tracker_host)) + new_torrent_filepath = tracker_handler.get_torrent_file() + if new_torrent_filepath is None: + self.dump_error(torrent_id, 'Error in tracker handling: %s' % tracker_handler.get_error_text()) + continue + + # Let's store cookies form that tracker to enter without logins in future sessions. + self.trackers_settings[tracker_handler.tracker_host]['cookies'] = tracker_handler.get_cookies(as_dict=True) + + new_torrent_contents = read_torrent_file(new_torrent_filepath) + new_torrent_info = read_torrent_info(new_torrent_contents) + if torrent_data['hash'] == new_torrent_info['hash']: + log.info('Updatorr \tSKIPPED Torrent is up-to-date') + continue + log.info('Updatorr \tTorrent update is available') + + new_torrent_prefs = get_new_prefs(torrent_data, new_torrent_info) + added_torrent_id = self.core.add_torrent_file(None, base64.encodestring(new_torrent_contents), new_torrent_prefs) + + if added_torrent_id is not None: + self.core.remove_torrent(torrent_id, False) + log.info('Updatorr \tTorrent is updated') + # Fire up update finished event. + component.get('EventManager').emit(UpdatorrUpdateDoneEvent(new_torrent_info['hash'])) + # Add new torrent hash to continue autoupdates. + self.set_items_to_update(new_torrent_info['hash'], True) + # Remove old torrent from autoupdates list. + self.set_items_to_update(torrent_id, False) + else: + self.dump_error(torrent_id, 'Unable to replace current torrent with a new one') + + # No littering, remove temporary .torrent file. + os.remove(new_torrent_filepath) + + if allow_last_walk_update: + # Remember lastrun time. + self.last_walk = time.time() + + log.info('Updatorr walk is finished') + component.get('EventManager').emit(UpdatorrUpdatesCheckFinishedEvent()) + except: + log.error(traceback.format_exc()) + finally: + self.walking = False def dump_error(self, torrent_id, text): """Logs error and fires error event.""" diff --git a/updatorr/utils.py b/updatorr/utils.py index 24c8e38..b394e51 100644 --- a/updatorr/utils.py +++ b/updatorr/utils.py @@ -7,6 +7,9 @@ from deluge._libtorrent import lt +import logging +log = logging.getLogger(__name__) + # Torrent tracker handler classes registry. TRACKER_HANDLERS = {} @@ -51,7 +54,7 @@ def read_torrent_info(file_contents): """ info_contents = lt.torrent_info(lt.bdecode(file_contents)) - files_from_torrent = [a_file.path for a_file in info_contents.files()] + files_from_torrent = [a_file.path.decode('utf-8') for a_file in info_contents.files()] info = {'hash': str(info_contents.info_hash()), 'files': files_from_torrent} return info @@ -66,6 +69,7 @@ def get_new_prefs(full_prefs, new_torrent_info): are copied from the previous session. """ + new_prefs = deepcopy(full_prefs) # These are some items we definitely do not want in a new session. @@ -88,28 +92,43 @@ def get_new_prefs(full_prefs, new_torrent_info): new_prefs['mapped_files'] = {} new_prefs['file_priorities'] = [] + # Check for root folder rename + new_files = new_torrent_info['files'] + old_root = _find_root([a_file['path'] for a_file in full_prefs['files']]) + if old_root is not None: + new_root = _find_root([os.path.dirname(a_file) for a_file in new_torrent_info['files']]) + if new_root is not None and new_root != old_root: + new_files = new_prefs['mapped_files'] + i = 0 + for a_file in new_torrent_info['files']: + if len(new_root) == 0: + new_prefs['mapped_files'][i] = os.path.join(old_root, a_file) + elif len(old_root) == 0: + new_prefs['mapped_files'][i] = a_file.replace(new_root, '', 1)[1:] # remove path separator as well + else: + new_prefs['mapped_files'][i] = a_file.replace(new_root, old_root, 1) + i += 1 + # Copying files priorities. old_priorities = get_files_priorities(full_prefs) - for a_file in new_torrent_info['files']: + for a_file in new_files: priority = 1 if a_file in old_priorities: priority = old_priorities[a_file] new_prefs['file_priorities'].append(priority) - # Check for root folder rename - old_splited = os.path.split(full_prefs['files'][0]['path']) - if len (old_splited) > 1: - old_folder = old_splited[0] - new_folder = os.path.split(new_torrent_info['files'][0])[0] - if new_folder != old_folder: - i = 0 - for a_file in new_torrent_info['files']: - new_prefs['mapped_files'][i] = a_file.replace(new_folder, old_folder) - i += 1 - - return new_prefs +def _find_root(folders): + """Returns common root folder (which can be empty string) or None if given folders don't have a common root folder""" + root = None + for folder in folders: + while not os.path.basename(folder) == folder: + folder = os.path.dirname(folder) + if root is not None: + if folder != root: return None + else: root = folder + return root def get_files_priorities(torrent_data): """Returns a dictionary with files priorities, where