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
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()