Example #1
0
    def _create_thumbnail_pixbuf(self, filepath):
        """ Creates a thumbnail pixbuf from <filepath>, and returns it as a
        tuple along with a file metadata dictionary: (pixbuf, tEXt_data) """

        if archive_tools.archive_mime_type(filepath) is not None:
            extractor = archive_extractor.Extractor()
            tmpdir = tempfile.mkdtemp(prefix=u'mcomix_archive_thumb.')
            condition = extractor.setup(filepath, tmpdir)
            files = extractor.get_files()
            wanted = self._guess_cover(files)

            if wanted:
                extractor.set_files([wanted])
                extractor.extract()
                image_path = os.path.join(tmpdir, wanted)

                condition.acquire()
                while not extractor.is_ready(wanted):
                    condition.wait()
                condition.release()

                if not os.path.isfile(image_path):
                    return None, None

                pixbuf = image_tools.load_pixbuf_size(image_path, self.width, self.height)
                tEXt_data = self._get_text_data(image_path)
                # Use the archive's mTime instead of the extracted file's mtime
                tEXt_data['tEXt::Thumb::MTime'] = str(long(os.stat(filepath).st_mtime))

                shutil.rmtree(tmpdir, True)
                return pixbuf, tEXt_data
            else:
                # Then check for subarchives by file extension and
                # extract only the first...
                subs = filter(constants.SUPPORTED_ARCHIVE_REGEX.search, files)
                if subs:
                    extractor.set_files([subs[0]])
                    extractor.extract()
                    condition.acquire()
                    while not extractor.is_ready(subs[0]):
                        condition.wait()
                    condition.release()
                    subpath = os.path.join(tmpdir, subs[0])
                    # Recursively try to find an image to use as cover
                    return self._create_thumbnail_pixbuf(subpath)

                shutil.rmtree(tmpdir, True)
                return None, None

        elif image_tools.is_image_file(filepath):
            pixbuf = image_tools.load_pixbuf_size(filepath, self.width, self.height)
            tEXt_data = self._get_text_data(filepath)

            return pixbuf, tEXt_data
        else:
            return None, None
Example #2
0
    def _create_thumbnail_pixbuf(self, filepath):
        """ Creates a thumbnail pixbuf from <filepath>, and returns it as a
        tuple along with a file metadata dictionary: (pixbuf, tEXt_data) """

        if self.archive_support:
            mime = archive_tools.archive_mime_type(filepath)
        else:
            mime = None
        if mime is not None:
            cleanup = []
            try:
                tmpdir = tempfile.mkdtemp(prefix=u'mcomix_archive_thumb.')
                cleanup.append(lambda: shutil.rmtree(tmpdir, True))
                archive = archive_tools.get_recursive_archive_handler(
                    filepath, tmpdir, type=mime)
                if archive is None:
                    return None, None
                cleanup.append(archive.close)
                files = archive.list_contents()
                wanted = self._guess_cover(files)
                if wanted is None:
                    return None, None

                archive.extract(wanted, tmpdir)

                image_path = os.path.join(tmpdir, wanted)
                if not os.path.isfile(image_path):
                    return None, None

                pixbuf = image_tools.load_pixbuf_size(image_path, self.width,
                                                      self.height)
                if self.store_on_disk:
                    tEXt_data = self._get_text_data(image_path)
                    # Use the archive's mTime instead of the extracted file's mtime
                    tEXt_data['tEXt::Thumb::MTime'] = str(
                        long(os.stat(filepath).st_mtime))
                else:
                    tEXt_data = None

                return pixbuf, tEXt_data
            finally:
                for fn in reversed(cleanup):
                    fn()

        elif image_tools.is_image_file(filepath):
            pixbuf = image_tools.load_pixbuf_size(filepath, self.width,
                                                  self.height)
            if self.store_on_disk:
                tEXt_data = self._get_text_data(filepath)
            else:
                tEXt_data = None

            return pixbuf, tEXt_data
        else:
            return None, None
Example #3
0
    def _create_thumbnail_pixbuf(self, filepath):
        """ Creates a thumbnail pixbuf from <filepath>, and returns it as a
        tuple along with a file metadata dictionary: (pixbuf, tEXt_data) """

        if self.archive_support:
            mime = archive_tools.archive_mime_type(filepath)
        else:
            mime = None
        if mime is not None:
            cleanup = []
            try:
                tmpdir = tempfile.mkdtemp(prefix=u'mcomix_archive_thumb.')
                cleanup.append(lambda: shutil.rmtree(tmpdir, True))
                archive = archive_tools.get_recursive_archive_handler(filepath,
                                                                      tmpdir,
                                                                      type=mime)
                if archive is None:
                    return None, None
                cleanup.append(archive.close)
                files = archive.list_contents()
                wanted = self._guess_cover(files)
                if wanted is None:
                    return None, None

                archive.extract(wanted, tmpdir)

                image_path = os.path.join(tmpdir, wanted)
                if not os.path.isfile(image_path):
                    return None, None

                pixbuf = image_tools.load_pixbuf_size(image_path, self.width, self.height)
                if self.store_on_disk:
                    tEXt_data = self._get_text_data(image_path)
                    # Use the archive's mTime instead of the extracted file's mtime
                    tEXt_data['tEXt::Thumb::MTime'] = str(long(os.stat(filepath).st_mtime))
                else:
                    tEXt_data = None

                return pixbuf, tEXt_data
            finally:
                for fn in reversed(cleanup):
                    fn()

        elif image_tools.is_image_file(filepath):
            pixbuf = image_tools.load_pixbuf_size(filepath, self.width, self.height)
            if self.store_on_disk:
                tEXt_data = self._get_text_data(filepath)
            else:
                tEXt_data = None

            return pixbuf, tEXt_data
        else:
            return None, None
    def open_file(self, path, start_page=0, keep_fileprovider=False):
        '''Open the file pointed to by <path>.

        If <start_page> is not set we set the current
        page to 1 (first page), if it is set we set the current page to the
        value of <start_page>. If <start_page> is non-positive it means the
        last image.

        Return True if the file is successfully loaded.
        '''

        self._close()

        try:
            path = self._initialize_fileprovider(path, keep_fileprovider)
        except ValueError as ex:
            self._window.statusbar.set_message(str(ex))
            self._window.osd.show(str(ex))
            return False

        error_message = self._check_access(path)
        if error_message:
            self._window.statusbar.set_message(error_message)
            self._window.osd.show(error_message)
            self.file_opened()
            return False

        self.filelist = self._file_provider.list_files()
        self.archive_type = archive_tools.archive_mime_type(path)
        self._start_page = start_page
        self._current_file = os.path.abspath(path)
        self._stop_waiting = False

        image_files = []
        current_image_index = 0

        # Actually open the file(s)/archive passed in path.
        if self.archive_type is not None:
            try:
                self._open_archive(self._current_file)
            except Exception as ex:
                self._window.statusbar.set_message(str(ex))
                self._window.osd.show(str(ex))
                self.file_opened()
                return False
            self.file_loading = True
        else:
            image_files, current_image_index = \
                self._open_image_files(self.filelist, self._current_file)
            self._archive_opened(image_files)

        return True
Example #5
0
    def _create_thumbnail_pixbuf(self, filepath):
        ''' Creates a thumbnail pixbuf from <filepath>, and returns it as a
        tuple along with a file metadata dictionary: (pixbuf, tEXt_data) '''

        if self.archive_support:
            mime = archive_tools.archive_mime_type(filepath)
        else:
            mime = None
        if mime is not None:
            if not archive_tools.is_archive_file(filepath):
                return None, None
            with archive_tools.get_recursive_archive_handler(
                    filepath, type=mime,
                    prefix='mcomix_archive_thumb.') as archive:
                if archive is None:
                    return None, None
                if archive.is_encrypted:
                    image_path = tools.pkg_path('images', 'encrypted-book.png')
                else:
                    files = archive.list_contents(decrypt=False)
                    wanted = self._guess_cover(files)
                    if wanted is None:
                        return None, None

                    image_path = archive.extract(wanted)
                    if not os.path.isfile(image_path):
                        return None, None

                pixbuf = image_tools.load_pixbuf_size(image_path, self.width,
                                                      self.height)
                if self.store_on_disk:
                    tEXt_data = self._get_text_data(image_path)
                    # Use the archive's mTime instead of the extracted file's mtime
                    tEXt_data['tEXt::Thumb::MTime'] = str(
                        os.stat(filepath).st_mtime)
                else:
                    tEXt_data = None

                return pixbuf, tEXt_data

        elif image_tools.is_image_file(filepath, check_mimetype=True):
            pixbuf = image_tools.load_pixbuf_size(filepath, self.width,
                                                  self.height)
            if self.store_on_disk:
                tEXt_data = self._get_text_data(filepath)
            else:
                tEXt_data = None

            return pixbuf, tEXt_data
        else:
            return None, None
Example #6
0
    def _create_thumbnail_pixbuf(self, filepath):
        """ Creates a thumbnail pixbuf from <filepath>, and returns it as a
        tuple along with a file metadata dictionary: (pixbuf, tEXt_data) """

        if self.archive_support:
            mime = archive_tools.archive_mime_type(filepath)
        else:
            mime = None
        if mime is not None:
            with tempfile.TemporaryDirectory(
                    prefix='mcomix_archive_thumb.') as tmpdir:
                archive = archive_tools.get_recursive_archive_handler(
                    filepath, tmpdir, type=mime)
                if archive is None:
                    return None, None
                files = archive.list_contents()
                wanted = self._guess_cover(files)
                if wanted is None:
                    return None, None

                archive.extract(wanted, tmpdir)

                image_path = os.path.join(tmpdir, wanted)
                if not os.path.isfile(image_path):
                    return None, None

                pixbuf = image_tools.load_pixbuf_size(image_path, self.width,
                                                      self.height)
                if self.store_on_disk:
                    tEXt_data = self._get_text_data(image_path)
                    # Use the archive's mTime instead of the extracted file's mtime
                    tEXt_data['tEXt::Thumb::MTime'] = str(
                        os.stat(filepath).st_mtime)
                else:
                    tEXt_data = None

                return pixbuf, tEXt_data

        elif image_tools.is_image_file(filepath):
            pixbuf = image_tools.load_pixbuf_size(filepath, self.width,
                                                  self.height)
            if self.store_on_disk:
                tEXt_data = self._get_text_data(filepath)
            else:
                tEXt_data = None

            return pixbuf, tEXt_data
        else:
            return None, None
    def test_archive_mime_type(self):

       dir = get_testfile_path('archives')
       for filename in os.listdir(dir):
           ext = '.'.join(filename.split('.')[1:])
           path = os.path.join(dir, filename)
           archive_type = archive_tools.archive_mime_type(path)
           expected_type = _EXTENSION_TO_MIME_TYPES[ext]
           msg = (
               'archive_mime_type("%s") failed; '
               'result differs: %s [%s] instead of %s [%s]'
               % (path,
                  archive_type, _ARCHIVE_TYPE_NAMES.get(archive_type, '???'),
                  expected_type, _ARCHIVE_TYPE_NAMES.get(expected_type, '???'),
                 )
           )
           self.assertEqual(archive_type, expected_type, msg=msg)
Example #8
0
    def test_archive_mime_type(self):

       dir = get_testfile_path('archives')
       for filename in os.listdir(dir):
           ext = '.'.join(filename.split('.')[1:])
           path = os.path.join(dir, filename)
           archive_type = archive_tools.archive_mime_type(path)
           expected_type = _EXTENSION_TO_MIME_TYPES.get(ext, '???')
           msg = (
               'archive_mime_type("%s") failed; '
               'result differs: %s [%s] instead of %s [%s]'
               % (path,
                  archive_type, _ARCHIVE_TYPE_NAMES.get(archive_type, '???'),
                  expected_type, _ARCHIVE_TYPE_NAMES.get(expected_type, '???'),
                 )
           )
           self.assertEqual(archive_type, expected_type, msg=msg)
Example #9
0
    def set_directory(self, file_or_directory):
        """ Sets the base directory. """

        if os.path.isdir(file_or_directory):
            self.base_dir = os.path.abspath(file_or_directory)
            self.mode = OrderedFileProvider.IMAGES
        elif os.path.isfile(file_or_directory):
            self.base_dir = os.path.abspath(os.path.dirname(file_or_directory))

            if image_tools.is_image_file(file_or_directory):
                self.mode = OrderedFileProvider.IMAGES
            elif archive_tools.archive_mime_type(file_or_directory) is not None:
                self.mode = OrderedFileProvider.ARCHIVES
            else:
                self.mode = OrderedFileProvider.IMAGES
        else:
            # Passed file doesn't exist
            raise ValueError(_("Invalid path: '%s'") % file_or_directory)
Example #10
0
    def set_directory(self, file_or_directory):
        """ Sets the base directory. """

        if os.path.isdir(file_or_directory):
            self.base_dir = os.path.abspath(file_or_directory)
            self.mode = OrderedFileProvider.IMAGES
        elif os.path.isfile(file_or_directory):
            self.base_dir = os.path.abspath(os.path.dirname(file_or_directory))

            if image_tools.is_image_file(file_or_directory):
                self.mode = OrderedFileProvider.IMAGES
            elif archive_tools.archive_mime_type(
                    file_or_directory) is not None:
                self.mode = OrderedFileProvider.ARCHIVES
            else:
                self.mode = OrderedFileProvider.IMAGES
        else:
            # Passed file doesn't exist
            raise ValueError(_("Invalid path: '%s'") % file_or_directory)
Example #11
0
    def _open_previous_archive(self, *args):
        """Open the archive that comes directly before the currently loaded
        archive in that archive's directory listing, sorted alphabetically.
        Returns True if a new archive was opened, False otherwise.
        """
        if self.archive_type is not None:

            files = self._file_provider.list_files(file_provider.FileProvider.ARCHIVES)
            absolute_path = os.path.abspath(self._base_path)
            if absolute_path not in files: return
            current_index = files.index(absolute_path)

            for path in reversed(files[:current_index]):
                if archive_tools.archive_mime_type(path) is not None:
                    self._close()
                    self.open_file(path, -1, keep_fileprovider=True)
                    return True

        return False
Example #12
0
    def _open_previous_archive(self, *args):
        """Open the archive that comes directly before the currently loaded
        archive in that archive's directory listing, sorted alphabetically.
        Returns True if a new archive was opened, False otherwise.
        """
        if self.archive_type is not None:

            files = self._file_provider.list_files(file_provider.FileProvider.ARCHIVES)
            absolute_path = os.path.abspath(self._base_path)
            if absolute_path not in files: return
            current_index = files.index(absolute_path)

            for path in reversed(files[:current_index]):
                if archive_tools.archive_mime_type(path) is not None:
                    self._close()
                    self.open_file(path, -1, keep_fileprovider=True)
                    return True

        return False
Example #13
0
    def _open_next_archive(self, *args):
        """Open the archive that comes directly after the currently loaded
        archive in that archive's directory listing, sorted alphabetically.
        Returns True if a new archive was opened, False otherwise.
        """
        if self.archive_type is not None:

            files = self._file_provider.list_files(file_provider.FileProvider.ARCHIVES)
            absolute_path = os.path.abspath(self._base_path)
            if absolute_path not in files: return
            current_index = files.index(absolute_path)

            for path in files[current_index + 1:]:
                if archive_tools.archive_mime_type(path) is not None:
                    self.close_file()
                    self._window.imagehandler.close()
                    self._window.scroll_to_fixed(horiz='startfirst', vert='top')
                    self.open_file(path, keep_fileprovider=True)
                    return True

        return False
Example #14
0
    def setup(self, src, dst, type=None):
        """Setup the extractor with archive <src> and destination dir <dst>.
        Return a threading.Condition related to the is_ready() method, or
        None if the format of <src> isn't supported.
        """
        self._src = src
        self._dst = dst
        self._type = type or archive_tools.archive_mime_type(src)
        self._files = []
        self._extracted = {}
        self._stop = False
        self._extract_thread = None
        self._condition = threading.Condition()

        self._archive = archive_tools.get_archive_handler(src)

        if self._archive:
            self._files = self._archive.list_contents()
            self._setupped = True
            return self._condition
        else:
            msg = _('Non-supported archive format: %s') % os.path.basename(src)
            log.warning(msg)
            raise ArchiveException(msg)
Example #15
0
    def setup(self, src, dst, type=None):
        """Setup the extractor with archive <src> and destination dir <dst>.
        Return a threading.Condition related to the is_ready() method, or
        None if the format of <src> isn't supported.
        """
        self._src = src
        self._dst = dst
        self._type = type or archive_tools.archive_mime_type(src)
        self._files = []
        self._extracted = {}
        self._stop = False
        self._extract_thread = None
        self._condition = threading.Condition()

        self._archive = archive_tools.get_archive_handler(src)

        if self._archive:
            self._files = self._archive.list_contents()
            self._setupped = True
            return self._condition
        else:
            msg = _('Non-supported archive format: %s') % os.path.basename(src)
            log.warning(msg)
            raise ArchiveException(msg)
Example #16
0
class FileHandler(object):
    """The FileHandler keeps track of the actual files/archives opened.

    While ImageHandler takes care of pages/images, this class provides
    the raw file names for archive members and image files, extracts
    archives, and lists directories for image files.
    """
    def __init__(self, window):
        #: Indicates if files/archives are currently loaded/loading.
        self.file_loaded = False
        self.file_loading = False
        #: None if current file is not an archive, or unrecognized format.
        self.archive_type = None

        #: Either path to the current archive, or first file in image list.
        #: This is B{not} the path to the currently open page.
        self._current_file = None
        #: Reference to L{MainWindow}.
        self._window = window
        #: Path to opened archive file, or directory containing current images.
        self._base_path = None
        #: Temporary directory used for extracting archives.
        self._tmp_dir = None
        #: If C{True}, no longer wait for files to get extracted.
        self._stop_waiting = False
        #: List of comment files inside of the currently opened archive.
        self._comment_files = []
        #: Mapping of absolute paths to archive path names.
        self._name_table = {}
        #: Archive extractor.
        self._extractor = archive_extractor.Extractor()
        self._extractor.file_extracted += self._extracted_file
        self._extractor.contents_listed += self._listed_contents
        #: Condition to wait on when extracting archives and waiting on files.
        self._condition = None
        #: Provides a list of available files/archives in the open directory.
        self._file_provider = None
        #: Keeps track of the last read page in archives
        self.last_read_page = last_read_page.LastReadPage(
            backend.LibraryBackend())
        #: Regexp used for determining which archive files are images.
        self._image_re = image_tools.SUPPORTED_IMAGE_REGEX
        #: Regexp used for determining which archive files are comment files.
        self._comment_re = None
        self.update_comment_extensions()

        self.last_read_page.set_enabled(bool(prefs['store recent file info']))

    def refresh_file(self, *args, **kwargs):
        """ Closes the current file(s)/archive and reloads them. """
        if self.file_loaded:
            current_file = os.path.abspath(
                self._window.imagehandler.get_real_path())
            if self.archive_type is not None:
                start_page = self._window.imagehandler.get_current_page()
            else:
                start_page = 0
            self.open_file(current_file, start_page, keep_fileprovider=True)

    def open_file(self, path, start_page=0, keep_fileprovider=False):
        """Open the file pointed to by <path>.

        If <start_page> is not set we set the current
        page to 1 (first page), if it is set we set the current page to the
        value of <start_page>. If <start_page> is non-positive it means the
        last image.

        Return True if the file is successfully loaded.
        """

        self._close()

        try:
            path = self._initialize_fileprovider(path, keep_fileprovider)
        except ValueError, ex:
            self._window.statusbar.set_message(unicode(ex))
            self._window.osd.show(unicode(ex))
            return False

        error_message = self._check_access(path)
        if error_message:
            self._window.statusbar.set_message(error_message)
            self._window.osd.show(error_message)
            self.file_opened()
            return False

        self.filelist = self._file_provider.list_files()
        self.archive_type = archive_tools.archive_mime_type(path)
        self._start_page = start_page
        self._current_file = os.path.abspath(path)
        self._stop_waiting = False

        image_files = []
        current_image_index = 0

        # Actually open the file(s)/archive passed in path.
        if self.archive_type is not None:
            try:
                self._open_archive(self._current_file)
            except Exception, ex:
                self._window.statusbar.set_message(unicode(ex))
                self._window.osd.show(unicode(ex))
                self.file_opened()
                return False
            self.file_loading = True
Example #17
0
    def _open_archive(self, path, start_page):
        """ Opens the archive passed in C{path}.

        Creates an L{archive_extractor.Extractor} and extracts all images
        found within the archive.

        @return: A tuple containing C{(image_files, image_index)}. """


        self._base_path = path
        try:
            self._condition = self._extractor.setup(self._base_path,
                                                self._tmp_dir,
                                                self.archive_type)
        except Exception:
            self._condition = None
            raise

        if self._condition != None:

            files = self._extractor.get_files()
            archive_images = [image for image in files
                if self._image_re.search(image)
                # Remove MacOS meta files from image list
                and not u'__MACOSX' in os.path.normpath(image).split(os.sep)]

            tools.alphanumeric_sort(archive_images)
            image_files = [ os.path.join(self._tmp_dir, f)
                            for f in archive_images ]

            comment_files = filter(self._comment_re.search, files)
            self._comment_files = [ os.path.join(self._tmp_dir, f)
                                    for f in comment_files ]

            # Allow managing sub-archives by keeping archives based on extension
            archive_files = filter(constants.SUPPORTED_ARCHIVE_REGEX.search, files)
            archive_files_paths = [ os.path.join(self._tmp_dir, f)
                                    for f in archive_files ]

            for name, full_path in zip(archive_images, image_files):
                self._name_table[full_path] = name

            for name, full_path in zip(comment_files, self._comment_files):
                self._name_table[full_path] = name

            for name, full_path in zip(archive_files, archive_files_paths):
                self._name_table[full_path] = name

            # Determine current archive image index.
            current_image_index = self._get_index_for_page(start_page,
                len(image_files), path)

            # Sort files to determine extraction order.
            self._sort_archive_files(archive_images, current_image_index)

            self._extractor.set_files(archive_images + comment_files + archive_files)
            self._extractor.file_extracted += self._extracted_file
            self._extractor.extract()

            # Manage subarchive through recursion
            if archive_files:
                has_subarchive = False

                # For each potential archive, change the current extractor,
                # extract recursively, and restore the internal extractor.
                for f in archive_files_paths:
                    if not self._extractor.is_ready(f):
                        self._wait_on_file(f)

                    if archive_tools.archive_mime_type(f) is not None:
                        # save self data
                        state = self._save_state()
                        # Setup temporary data
                        self._extractor = archive_extractor.Extractor()
                        self._tmp_dir = os.path.join(self._tmp_dir,
                            os.path.basename(f) + u'.dir')
                        if not os.path.exists(self._tmp_dir):
                            os.mkdir(self._tmp_dir)
                        self._condition = self._extractor.setup(self._base_path,
                                                self._tmp_dir,
                                                self.archive_type)
                        self._extractor.file_extracted += self._extracted_file
                        add_images, dummy_idx = self._open_archive(f, 1) # recursion here
                        # Since it's recursive, we do not want to loose the way to ensure
                        # that the file was extracted, so too bad but it will be a lil' slower.
                        for image in add_images:
                            self._wait_on_file(image)
                        image_files.extend(add_images)
                        self._extractor.stop()
                        self._extractor.close()
                        # restore self data
                        self._restore_state(state)
                        has_subarchive = True

                # Allows to avoid any behaviour changes if there was no subarchive..
                if has_subarchive:
                    # Mark additional files as extracted
                    self._comment_files = \
                        filter(self._comment_re.search, image_files)
                    tmp_image_files = \
                        filter(self._image_re.search, image_files)
                    self._name_table.clear()
                    for full_path in tmp_image_files + self._comment_files:
                        self._name_table[full_path] = os.path.basename(full_path)
                        # This trick here allows to avoid indefinite waiting on
                        # the sub-extracted files.
                        self._extractor._extracted[os.path.basename(full_path)] = True
                    # set those files instead of image_files for the return
                    image_files = tmp_image_files

            # Image index may have changed after additional files were extracted.
            current_image_index = self._get_index_for_page(start_page,
                len(image_files), path)

            return image_files, current_image_index

        else:
            # No condition was returned from the Extractor, i.e. invalid archive.
            return [], 0
Example #18
0
class FileHandler(object):

    """The FileHandler keeps track of the actual files/archives opened.

    While ImageHandler takes care of pages/images, this class provides
    the raw file names for archive members and image files, extracts
    archives, and lists directories for image files.
    """

    def __init__(self, window):
        #: Indicates if files/archives are currently loaded.
        self.file_loaded = False
        #: None if current file is not an archive, or unrecognized format.
        self.archive_type = None

        #: Either path to the current archive, or first file in image list.
        #: This is B{not} the path to the currently open page.
        self._current_file = None
        #: Reference to L{MainWindow}.
        self._window = window
        #: Path to opened archive file, or directory containing current images.
        self._base_path = None
        #: Temporary directory used for extracting archives.
        self._tmp_dir = tempfile.mkdtemp(prefix=u'mcomix.', suffix=os.sep)
        #: If C{True}, no longer wait for files to get extracted.
        self._stop_waiting = False
        #: List of comment files inside of the currently opened archive.
        self._comment_files = []
        #: Mapping of absolute paths to archive path names.
        self._name_table = {}
        #: Archive extractor.
        self._extractor = archive_extractor.Extractor()
        #: Condition to wait on when extracting archives and waiting on files.
        self._condition = None
        #: Provides a list of available files/archives in the open directory.
        self._file_provider = None
        #: Keeps track of the last read page in archives
        self.last_read_page = last_read_page.LastReadPage(constants.LASTPAGE_DATABASE_PATH)
        #: Regexp used for determining which archive files are images.
        self._image_re = constants.SUPPORTED_IMAGE_REGEX
        #: Regexp used for determining which archive files are comment files.
        self._comment_re = None
        self.update_comment_extensions()

        self.last_read_page.set_enabled(
            prefs['store recent file info'] == constants.STORE_LAST_PATH_AND_PAGE)

    def refresh_file(self, *args, **kwargs):
        """ Closes the current file(s)/archive and reloads them. """
        if self.file_loaded:
            current_file = os.path.abspath(self._window.imagehandler.get_real_path())
            self.open_file(current_file, keep_fileprovider=True)

    def open_file(self, path, start_page=0, keep_fileprovider=False):
        """Open the file pointed to by <path>.

        If <start_page> is not set we set the current
        page to 1 (first page), if it is set we set the current page to the
        value of <start_page>. If <start_page> is non-positive it means the
        last image.

        Return True if the file is successfully loaded.
        """

        try:
            path = self._initialize_fileprovider(path, keep_fileprovider)
        except ValueError, ex:
            self._window.statusbar.set_message(unicode(ex))
            self._window.osd.show(unicode(ex))
            return False

        if os.path.exists(path) and os.access(path, os.R_OK):
            filelist = self._file_provider.list_files()
            archive_type = archive_tools.archive_mime_type(path)
        else:
            filelist = []
            archive_type = None

        error_message = self._check_for_error_message(path, filelist, archive_type)
        if error_message:
            self._window.statusbar.set_message(error_message)
            self._window.osd.show(error_message)
            return False

        # We close the previously opened file.
        if self.file_loaded:
            self.close_file()

        # Catch up on UI events before actually starting to open the file(s).
        while gtk.events_pending():
            gtk.main_iteration(False)

        self.archive_type = archive_type
        self._current_file = os.path.abspath(path)
        self._stop_waiting = False

        result = False
        image_files = []
        current_image_index = 0

        # Actually open the file(s)/archive passed in path.
        if self.archive_type is not None:
            try:
                image_files, current_image_index = \
                    self._open_archive(self._current_file, start_page)
            except Exception, ex:
                self.file_loaded = False
                self._window.statusbar.set_message(unicode(ex))
                self._window.osd.show(unicode(ex))
                self._window.uimanager.set_sensitivities()
                return False

            # Update status bar
            archive_list = self._file_provider.list_files(
                file_provider.FileProvider.ARCHIVES)
            if self._current_file in archive_list:
                current_index = archive_list.index(self._current_file)
            else:
                current_index = 0

            self._window.statusbar.set_file_number(current_index + 1,
                len(archive_list))
Example #19
0
class FileHandler(object):
    """The FileHandler keeps track of the actual files/archives opened.

    While ImageHandler takes care of pages/images, this class provides
    the raw file names for archive members and image files, extracts
    archives, and lists directories for image files.
    """
    def __init__(self, window):
        #: Indicates if files/archives are currently loaded/loading.
        self.file_loaded = False
        self.file_loading = False
        #: None if current file is not an archive, or unrecognized format.
        self.archive_type = None

        self._to_delete = {}
        self._did_split = False

        #: Either path to the current archive, or first file in image list.
        #: This is B{not} the path to the currently open page.
        self._current_file = None
        #: Reference to L{MainWindow}.
        self._window = window
        #: Path to opened archive file, or directory containing current images.
        self._base_path = None
        #: Temporary directory used for extracting archives.
        self._tmp_dir = None
        #: If C{True}, no longer wait for files to get extracted.
        self._stop_waiting = False
        #: List of comment files inside of the currently opened archive.
        self._comment_files = []
        #: Mapping of absolute paths to archive path names.
        self._name_table = {}
        #: Archive extractor.
        self._extractor = archive_extractor.Extractor()
        self._extractor.file_extracted += self._extracted_file
        self._extractor.contents_listed += self._listed_contents
        #: Condition to wait on when extracting archives and waiting on files.
        self._condition = None
        #: Provides a list of available files/archives in the open directory.
        self._file_provider = None
        #: Keeps track of the last read page in archives
        self.last_read_page = last_read_page.LastReadPage(
            backend.LibraryBackend())
        #: Regexp used for determining which archive files are images.
        self._image_re = image_tools.SUPPORTED_IMAGE_REGEX
        #: Regexp used for determining which archive files are comment files.
        self._comment_re = None
        self.update_comment_extensions()

        self.last_read_page.set_enabled(bool(prefs['store recent file info']))

    def refresh_file(self, *args, **kwargs):
        """ Closes the current file(s)/archive and reloads them. """
        if self.file_loaded:
            current_file = os.path.abspath(
                self._window.imagehandler.get_real_path())
            if self.archive_type is not None:
                start_page = self._window.imagehandler.get_current_page()
            else:
                start_page = 0
            self.open_file(current_file, start_page, keep_fileprovider=True)

    def delete_file(self, archive_file_name):
        if archive_file_name in self._to_delete and self._to_delete[
                archive_file_name]:
            print "undeleting %s" % (archive_file_name)
            self._to_delete[archive_file_name] = False
            return False
        else:
            print "deleting %s" % (archive_file_name)
            self._to_delete[archive_file_name] = True
            return True

    def should_split_images(self):
        return not self._did_split

    def split_images(self):
        if self._did_split:
            print "Already split, yo"
            return

        # NOTE: this can happen on startup
        if len(self._name_table) == 0:
            return

        self._did_split = True

        filenames = [k for k in self._name_table]

        # TODO: hyper unreliable
        exe_name = os.path.join(os.path.dirname(sys.argv[0]), "bins",
                                "comic_splitter")

        if not os.path.exists(exe_name):
            print base + " doesn't exist!"
            return

        print(exe_name)
        output_dirname = os.path.dirname(filenames[0])

        # print filenames
        print "Splitting at %s..." % (output_dirname)
        subprocess.call([exe_name, "-output", output_dirname, "-delete"] +
                        filenames)

        # prefix = os.path.basename(output_dirname)
        # prefix = os.path.basename(output_dirname)
        # print prefix
        prefix = output_dirname
        files = [
            os.path.join(prefix, f) for f in os.listdir(output_dirname)
            if os.path.isfile(os.path.join(output_dirname, f))
        ]

        archive_images = [
            image for image in files if self._image_re.search(image)
            # Remove MacOS meta files from image list
            and not u'__MACOSX' in os.path.normpath(image).split(os.sep)
        ]

        self._sort_archive_images(archive_images)

        image_files = [os.path.join(self._tmp_dir, f) for f in archive_images]

        comment_files = filter(self._comment_re.search, files)
        tools.alphanumeric_sort(comment_files)
        self._comment_files = [
            os.path.join(self._tmp_dir, f) for f in comment_files
        ]

        self._name_table = dict(zip(files, archive_images))
        self._name_table.update(zip(self._comment_files, comment_files))

        # self.file_available(self.filelist, True)

        self._archive_opened(image_files)
        self.filelist = files
        self._window.imagehandler._available_images = set(
            range(0, len(image_files)))
        self._window.imagehandler._image_files = image_files
        # self._extractor.set_files(archive_images + comment_files)
        return True

    def _actually_delete_files(self):
        to_delete = [
            path for (path, value) in self._to_delete.items() if value
        ]
        print "actually deleting %d files..." % (len(to_delete))
        # TODO: ask if the user actually wants to delete those files
        if len(to_delete) > 0:
            to_delete_str = " ".join(["\"%s\"" % (path) for path in to_delete])
            os.system("zip \"%s\" --delete %s" %
                      (self.get_current_file(), to_delete_str))
            self._to_delete.clear()

    def open_file(self, path, start_page=0, keep_fileprovider=False):
        """Open the file pointed to by <path>.

        If <start_page> is not set we set the current
        page to 1 (first page), if it is set we set the current page to the
        value of <start_page>. If <start_page> is non-positive it means the
        last image.

        Return True if the file is successfully loaded.
        """

        self._close()

        try:
            path = self._initialize_fileprovider(path, keep_fileprovider)
        except ValueError, ex:
            self._window.statusbar.set_message(unicode(ex))
            self._window.osd.show(unicode(ex))
            return False

        error_message = self._check_access(path)
        if error_message:
            self._window.statusbar.set_message(error_message)
            self._window.osd.show(error_message)
            self.file_opened()
            return False

        self.filelist = self._file_provider.list_files()
        self.archive_type = archive_tools.archive_mime_type(path)
        self._start_page = start_page
        self._current_file = os.path.abspath(path)
        self._stop_waiting = False

        image_files = []
        current_image_index = 0

        # Actually open the file(s)/archive passed in path.
        if self.archive_type is not None:
            try:
                self._open_archive(self._current_file)
            except Exception, ex:
                self._window.statusbar.set_message(unicode(ex))
                self._window.osd.show(unicode(ex))
                self.file_opened()
                return False
            self.file_loading = True