예제 #1
0
    def __init__(self, dir, subgals, album, album_dest_dir, progress=None):
        self.source_dir = dir
        self.path = os.path.join(album_dest_dir, self.source_dir.strip_root())
        if self.path.endswith(os.sep): self.path = os.path.dirname(self.path)

        super(WebalbumDir, self).__init__(self.path)

        self.add_dependency(self.source_dir)
        self.subgals = subgals
        self.album = album
        self.feed = None

        self.flattening_dir = None

        self.config = LazygalWebgalConfig(self.album.config)
        self.__configure()

        # Create the directory if it does not exist
        if not os.path.isdir(self.path) and (self.source_dir.get_media_count() > 0):
            logging.info(_("  MKDIR %%WEBALBUMROOT%%/%s")
                         % self.source_dir.strip_root())
            logging.debug("(%s)" % self.path)
            os.makedirs(self.path, mode=0755)
            # Directory did not exist, mark it as so
            self.stamp_delete()

        tagfilters = self.config.getlist('webgal', 'filter-by-tag')

        self.medias = []
        self.sort_task = SubgalSort(self)
        self.sort_task.add_dependency(self.source_dir)
        for media in self.source_dir.medias:
            self.sort_task.add_dependency(media)

            if len(tagfilters) > 0 and media.info() is not None:
                # tag-filtering is requested
                res = True
                for tagf in tagfilters:
                    # concatenate the list of tags as a string of words,
                    # space-separated.  to ensure that we match the full
                    # keyword and not only a subpart of it, we also surround
                    # the matching pattern with spaces

                    # we look for tag words, partial matches are not wanted
                    regex = re.compile(r"\b" + tagf + r"\b")

                    kwlist = ' '.join(media.info().get_keywords())
                    if re.search(regex, kwlist) is None:
                        res = False
                        break
                if res is False:
                    continue

            if media.type == 'image':
                media_task = WebalbumImageTask(self, media)
            elif media.type == 'video':
                media_task = WebalbumVideoTask(self, media)
            else:
                raise NotImplementedError("Unknown media type '%s'"
                                          % media.type)
            self.medias.append(media_task)
            self.add_dependency(media_task)

        if self.config.getboolean('webgal', 'dirzip')\
                and self.get_media_count() > 1:
            self.dirzip = genfile.WebalbumArchive(self)
            self.add_dependency(self.dirzip)
        else:
            self.dirzip = None

        self.index_pages = []

        if not self.should_be_flattened():
            self.break_task = SubgalBreak(self)

            if self.thumbs_per_page > 0:
                # FIXME: If pagination is 'on', galleries need to be sorted
                # before being broken on multiple pages, and thus this slows
                # down a lot the checking of a directory's need to be built.
                self.break_task.add_dependency(self.sort_task)

            # This task is special because it populates dependencies. This is
            # why it needs to be built before a build check.
            self.break_task.make()

            self.webgal_pic = genmedia.WebalbumPicture(self)
            self.add_dependency(self.webgal_pic)
        else:
            self.break_task = None

        self.progress = progress
예제 #2
0
class WebalbumDir(make.FileMakeObject):
    """
    This is a built web gallery with its files, thumbs and reduced pics.
    """

    def __init__(self, dir, subgals, album, album_dest_dir, progress=None):
        self.source_dir = dir
        self.path = os.path.join(album_dest_dir, self.source_dir.strip_root())
        if self.path.endswith(os.sep): self.path = os.path.dirname(self.path)

        super(WebalbumDir, self).__init__(self.path)

        self.add_dependency(self.source_dir)
        self.subgals = subgals
        self.album = album
        self.feed = None

        self.flattening_dir = None

        self.config = LazygalWebgalConfig(self.album.config)
        self.__configure()

        # Create the directory if it does not exist
        if not os.path.isdir(self.path) and (self.source_dir.get_media_count() > 0):
            logging.info(_("  MKDIR %%WEBALBUMROOT%%/%s")
                         % self.source_dir.strip_root())
            logging.debug("(%s)" % self.path)
            os.makedirs(self.path, mode=0755)
            # Directory did not exist, mark it as so
            self.stamp_delete()

        tagfilters = self.config.getlist('webgal', 'filter-by-tag')

        self.medias = []
        self.sort_task = SubgalSort(self)
        self.sort_task.add_dependency(self.source_dir)
        for media in self.source_dir.medias:
            self.sort_task.add_dependency(media)

            if len(tagfilters) > 0 and media.info() is not None:
                # tag-filtering is requested
                res = True
                for tagf in tagfilters:
                    # concatenate the list of tags as a string of words,
                    # space-separated.  to ensure that we match the full
                    # keyword and not only a subpart of it, we also surround
                    # the matching pattern with spaces

                    # we look for tag words, partial matches are not wanted
                    regex = re.compile(r"\b" + tagf + r"\b")

                    kwlist = ' '.join(media.info().get_keywords())
                    if re.search(regex, kwlist) is None:
                        res = False
                        break
                if res is False:
                    continue

            if media.type == 'image':
                media_task = WebalbumImageTask(self, media)
            elif media.type == 'video':
                media_task = WebalbumVideoTask(self, media)
            else:
                raise NotImplementedError("Unknown media type '%s'"
                                          % media.type)
            self.medias.append(media_task)
            self.add_dependency(media_task)

        if self.config.getboolean('webgal', 'dirzip')\
                and self.get_media_count() > 1:
            self.dirzip = genfile.WebalbumArchive(self)
            self.add_dependency(self.dirzip)
        else:
            self.dirzip = None

        self.index_pages = []

        if not self.should_be_flattened():
            self.break_task = SubgalBreak(self)

            if self.thumbs_per_page > 0:
                # FIXME: If pagination is 'on', galleries need to be sorted
                # before being broken on multiple pages, and thus this slows
                # down a lot the checking of a directory's need to be built.
                self.break_task.add_dependency(self.sort_task)

            # This task is special because it populates dependencies. This is
            # why it needs to be built before a build check.
            self.break_task.make()

            self.webgal_pic = genmedia.WebalbumPicture(self)
            self.add_dependency(self.webgal_pic)
        else:
            self.break_task = None

        self.progress = progress

    def __parse_browse_sizes(self, sizes_string):
        for single_def in sizes_string.split(','):
            name, string_size = single_def.split('=')
            name = name.decode(locale.getpreferredencoding())
            if name == '':
                raise ValueError(_("Sizes is a comma-separated list of size names and specs:\n\t e.g. \"small=640x480,medium=1024x768\"."))
            if name == genmedia.THUMB_SIZE_NAME:
                raise ValueError(_("Size name '%s' is reserved for internal processing.") % genmedia.THUMB_SIZE_NAME)
            self.__parse_size(name, string_size)
            self.browse_sizes.append(name)

    def __parse_size(self, size_name, size_string):
        if size_string == '0x0':
            self.newsizers[size_name] = 'original'
        else:
            try:
                self.newsizers[size_name] = newsize.get_newsizer(size_string)
            except newsize.NewsizeStringParseError:
                raise ValueError(_("'%s' for size '%s' does not describe a known size syntax.") % (size_string.decode(locale.getpreferredencoding()), size_name, ))

    def __parse_sort(self, sort_string):
        try:
            sort_method, reverse = sort_string.split(':')
        except ValueError:
            sort_method = sort_string
            reverse = False
        if reverse == 'reverse':
            return sort_method, True
        else:
            return sort_method, False

    def __load_tpl_vars(self):
        # Load tpl vars from config
        tpl_vars = {}
        if self.config.has_section('template-vars'):
            tpl_vars = {}
            for option in self.config.options('template-vars'):
                try:
                    value = self.config.getboolean('template-vars', option)
                    tpl_vars[option] = value
                except ValueError:
                    value = self.config.get('template-vars', option)
                    value = value.decode(locale.getpreferredencoding())
                    tpl_vars[option] = genshi.core.Markup(value)

        return tpl_vars

    def __configure(self):
        config_dirs = self.source_dir.parent_paths()[:-1]  # strip root dir
        config_dirs.reverse()  # from root to deepest
        config_files = map(lambda d: os.path.join(d, SOURCEDIR_CONFIGFILE),
                           config_dirs)
        logging.debug(_("  Trying loading gallery configs: %s")
                      % ', '.join(map(self.source_dir.strip_root,
                                      config_files)))
        self.config.read(config_files)

        self.browse_sizes = []
        self.newsizers = {}
        self.__parse_browse_sizes(self.config.get('webgal', 'image-size'))
        self.__parse_size(genmedia.THUMB_SIZE_NAME,
                          self.config.get('webgal', 'thumbnail-size'))
        self.default_size_name = self.browse_sizes[0]

        self.tpl_vars = self.__load_tpl_vars()
        styles = self.album.theme.get_avail_styles(
            self.config.get('webgal', 'default-style'))
        self.tpl_vars.update({'styles': styles})

        self.set_original(self.config.getboolean('webgal', 'original'),
                          self.config.getstr('webgal', 'original-baseurl'),
                          self.config.getboolean('webgal', 'original-symlink'))

        self.thumbs_per_page = self.config.getint('webgal', 'thumbs-per-page')

        self.quality = self.config.getint('webgal', 'jpeg-quality')
        self.save_options = {}
        if self.config.getboolean('webgal', 'jpeg-optimize'):
            self.save_options['optimize'] = True
        if self.config.getboolean('webgal', 'jpeg-progressive'):
            self.save_options['progressive'] = True

        self.pic_sort_by = self.__parse_sort(self.config.get('webgal', 'sort-medias'))
        self.subgal_sort_by = self.__parse_sort(self.config.get('webgal', 'sort-subgals'))
        self.filter_by_tag = self.config.get('webgal', 'filter-by-tag')

        self.webalbumpic_bg = self.config.get('webgal', 'webalbumpic-bg')
        self.webalbumpic_type = self.config.get('webgal', 'webalbumpic-type')
        try:
            self.webalbumpic_size = map(int, self.config.get('webgal', 'webalbumpic-size').split('x'))
            if len(self.webalbumpic_size) != 2:
                raise ValueError
        except ValueError:
            logging.error(_('Bad syntax for webalbumpic-size.'))
            sys.exit(1)
        self.keep_gps = self.config.getboolean('webgal', 'keep-gps')

    def set_original(self, original=False, orig_base=None, orig_symlink=False):
        self.original = original or orig_symlink
        self.orig_symlink = orig_symlink
        if self.original and orig_base and not orig_symlink:
            self.orig_base = orig_base
        else:
            self.orig_base = None

    def get_webalbumpic_filename(self):
        if self.webalbumpic_bg == 'transparent':
            ext = '.png'  # JPEG does not have an alpha channel
        else:
            ext = '.jpg'
        return genmedia.WebalbumPicture.BASEFILENAME + ext

    def _add_size_qualifier(self, path, size_name, force_extension=None):
        filename, extension = os.path.splitext(path)
        if force_extension is not None:
            extension = force_extension

        if size_name == self.default_size_name and extension == '.html':
            # Do not append default size name to HTML page filename
            return path
        elif size_name in self.browse_sizes\
                and self.newsizers[size_name] == 'original'\
                and extension != '.html':
            # Do not append size_name to unresized images.
            return path
        else:
            return "%s_%s%s" % (filename, size_name, extension)

    def add_index_page(self, subgals, galleries):
        page_number = self.break_task.next_page_number()
        pages = []
        for size_name in self.browse_sizes:
            page = genpage.WebalbumIndexPage(self, size_name, page_number,
                                             subgals, galleries)
            if self.album.force_gen_pages:
                page.stamp_delete()
            self.add_dependency(page)
            pages.append(page)
        self.index_pages.append(pages)

    def register_output(self, output):
        # We only care about output in the current directory
        if os.path.dirname(output) == self.path:
            super(WebalbumDir, self).register_output(output)

    def register_feed(self, feed):
        self.feed = feed

    def get_subgal_count(self):
        if self.flatten_below():
            return 0
        else:
            len(self.source_dir.subdirs)

    def get_all_subgals(self):
        all_subgals = list(self.subgals)  # We want a copy here.
        for subgal in self.subgals:
            all_subgals.extend(subgal.get_all_subgals())
        return all_subgals

    def get_media_count(self, media_type=None):
        if media_type is None:
            return len(self.medias)
        else:
            typed_media_count = 0
            for mediatask in self.medias:
                if mediatask.media.type == media_type:
                    typed_media_count += 1
            return typed_media_count

    def get_all_medias_tasks(self):
        all_medias = list(self.medias)  # We want a copy here.
        for subgal in self.subgals:
            all_medias.extend(subgal.get_all_medias_tasks())
        return all_medias

    def should_be_flattened(self, path=None):
        if path is None: path = self.source_dir.path
        return self.album.dir_flattening_depth is not False\
            and self.source_dir.get_album_level(path) > self.album.dir_flattening_depth

    def flatten_below(self):
        if self.album.dir_flattening_depth is False:
            return False
        elif len(self.source_dir.subdirs) > 0:
            # As all subdirs are at the same level, if one should be flattened,
            # all should.
            return self.subgals[0].should_be_flattened()
        else:
            return False

    def rel_path_to_src(self, target_srcdir_path):
        """
        Returns the relative path to go from this directory to
        target_srcdir_path.
        """
        return self.source_dir.rel_path(self.source_dir.path,
                                        target_srcdir_path)

    def rel_path(self, path):
        """
        Returns the relative path to go from this directory to the path
        supplied as argument.
        """
        return os.path.relpath(path, self.path)

    def flattening_srcpath(self, srcdir_path):
        """
        Returns the source path in which srcdir_path should flattened, that is
        the path of the gallery index that will point to srcdir_path's
        pictures.
        """
        if self.should_be_flattened(srcdir_path):
            cur_path = srcdir_path
            while self.should_be_flattened(cur_path):
                cur_path, dummy = os.path.split(cur_path)
            return cur_path
        else:
            return ''

    def list_foreign_files(self):
        foreign_files = []

        # Check dest for junk files
        extra_files = []
        if self.source_dir.is_album_root():
            extra_files.append(os.path.join(self.path,
                                            DEST_SHARED_DIRECTORY_NAME))

        dirnames = [d.name for d in self.source_dir.subdirs]
        expected_dirs = map(lambda dn: os.path.join(self.path, dn), dirnames)
        for dest_file in os.listdir(self.path):
            dest_file = os.path.join(self.path, dest_file)
            if not isinstance(dest_file, unicode):
                # FIXME: No clue why this happens, but it happens!
                dest_file = dest_file.decode(sys.getfilesystemencoding())
            if dest_file not in self.output_items and\
                dest_file not in expected_dirs and\
                    dest_file not in extra_files:
                foreign_files.append(dest_file)

        return foreign_files

    def build(self):
        for dest_file in self.list_foreign_files():
            self.album.cleanup(dest_file)

    def make(self, force=False):
        needed_build = self.needs_build()

        super(WebalbumDir, self).make(force or needed_build)

        # Although we should have modified the directory contents and thus its
        # mtime, it is possible that the directory mtime has not been updated
        # if we regenerated without adding/removing pictures (to take into
        # account a rotation for example). This is why we force directory mtime
        # update here if something has been built.
        if needed_build: os.utime(self.path, None)

    def media_done(self):
        if self.progress is not None:
            self.progress.media_done()