Commit 56b31a81 authored by SVN-Git Migration's avatar SVN-Git Migration

Imported Upstream version 0.9

parent ae4e9f15
Lazygal 0.9 (2016-11-01)
* rename default theme as nojs
* warn of no video support only if videos are found
* silence some debug messages (Debian Closes: #836697)
* Fix crash if video deps are not installed (#21)
* fix crash when using -z
* build a JSON index for each webgal
* gst: avoid failure when Gst.init has not been called yet
* gi bindings: require version as advised by gi lib
* Fix #19 JSON config file in album source not loaded
* fix failure to generate exif sorted gallery (Debian Closes: #794899)
* spawn hg instead of using mercurial python api for guessing dev revision
* remove existing symlinks when switching from --orig-symlink to -O
* Fix #18 warn if python-gst-1.0 overrides are not installed
* Fix #17 and make setup.py install the new default conf file
* make python3 the default
* inverted theme: do not distort images (Debian #782376)
* merge fix for #16
* rename TranscodeError to VideoError (end Fix #16)
* support a new JSON configuration file format
* ensure obeying to umask setting (Debian Closes: #776195)
* fix exception when run on empty dir (Debian Closes: #776198)
* add '--preserve PATTERN', to prevent removal of files/directories
* add '--exclude PATTERN', to allow ignoring some files/directories
* add 'numeric' sort criteria for media and galleries
* update italian translation
Lazygal 0.8.8 (2014-10-22)
* silence gobject assertions errors at startup
* fix CTRL+C not working after a video has been transcoded
......
Lazygal 0.9 (2016-11-01)
* rename default theme as nojs
* warn of no video support only if videos are found
* silence some debug messages (Debian Closes: #836697)
* Fix crash if video deps are not installed (#21)
* fix crash when using -z
* introduce tags of interest for JSON metadata
* no need to build sample albums for testing conf
* fix video path when subgal is included in parent page
* py3: map is lazy, fix syntax
* correctly report progress upon video transcoding abort
* correctly report progress upon skipped media
* build a JSON index for each webgal
* gst: avoid failure when Gst.init has not been called yet
* gi bindings: require version as advised by gi lib
* Fix #19 JSON config file in album source not loaded
* update defaults conf reference to use new JSON filename
* fix html template vars wrongly escaped regression
* fix typo in JSON conf example
* fail if given an unknown default-style
* author bad encoding: do not fail with pythn3 and GExiv2 < 0.10.3
* fix failure to generate exif sorted gallery (Debian Closes: #794899)
* fix exif date time tag names
* handle failure to copy metadata tag
* video transcoding: do not crash when media_duration is not known yet
* fix conf backward compat with python2
* fix root config file values being overriden by defaults
* spawn hg instead of using mercurial python api for guessing dev revision
* configparser.readfp() is deprecated in python > 3.2
* remove existing symlinks when switching from --orig-symlink to -O
* Fix #18 warn if python-gst-1.0 overrides are not installed
* Fix #17 and make setup.py install the new default conf file
* make python3 the default
* inverted theme: do not distort images (Debian #782376)
* merge fix for #16
* rename TranscodeError to VideoError (end Fix #16)
* add a unittest to basically test video generation
* GObject.thread_init() is deprecated, remove
* support a new JSON configuration file format
* unittests: improve failing message
* sort webgal only after filtered medias have been filtered out
* load tagfilters only once
* fix unit test regarding album_picture being relative
* ensure obeying to umask setting (Debian Closes: #776195)
* cache media size probing
* py3: correctly decode metadata tags
* ensure album_picture parameter is always relative
* migrate RSS20 class to new style classes
* correctly close open file descriptors
* make it easier to understand why a video thumbnail is not generated
* don't check twice if size is 'original'
* remove erroneous leading comma in progress message
* fix exception when run on empty dir (Debian Closes: #776198)
* simplify excludes implementation (and unbreak test suite)
* fix broken line continuations
* add '--preserve PATTERN', to prevent removal of files/directories
* add '--exclude PATTERN', to allow ignoring some files/directories
* numeric sort option: optimizations
* manpage reformating regarding new numeric sort option
* add 'numeric' sort criteria for media and galleries
* update italian translation
* merge version
* py3: fix symlink directory cleanup after generation
* test out of tree symlinks and cleanup
* minor indent fix
Lazygal 0.8.8 (2014-10-22)
* set version to 0.8.8
* silence gobject assertions errors at startup
* fix pipelines in comments and use gst1
* port to gst1 (additional correction)
......
......@@ -22,3 +22,4 @@ include devscripts/*
include userscripts/*
include lazygaltest/*py
include lazygaltest/sample*.jpg
include lazygaltest/vid.mov
Metadata-Version: 1.1
Name: lazygal
Version: 0.8.8
Version: 0.9
Summary: Static web gallery generator
Home-page: http://sousmonlit.zincube.net/~niol/playa/oss/projects/lazygal
Author: Alexandre Rossi
......
......@@ -181,7 +181,34 @@
</term>
<listitem>
<para>Clean destination directory of files that should not
be there (default is to print a warning but not to delete).</para>
be there.</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<option>--preserve=<replaceable>PATTERN</replaceable></option>
</term>
<listitem>
<para>Specify a file pattern (or name) which should be
ignored during cleanup of the destination. May be specified
more than once.
Values given here will be in addition to those specified in
configuration files.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<option>--excludes=<replaceable>PATTERN</replaceable></option>
</term>
<listitem>
<para>Specify a file pattern (or name) which should be
ignored during processing. May be specified more than once.
Values given here will be in addition to those specified in
configuration files.
</para>
</listitem>
</varlistentry>
......@@ -328,9 +355,11 @@
</term>
<listitem>
<para>Sort order for images in a subgallery, among 'mtime',
'filename', or 'exif'. (default is 'exif' which is by EXIF date if
EXIF data is available, filename otherwise, sorting EXIF-less
images before). Add ':reverse' to reverse the sort order
'filename', 'numeric', or 'exif'. (default is 'exif' which is by
EXIF date if EXIF data is available, filename otherwise,
sorting EXIF-less images before). 'numeric' does a numeric sort on
the numeric part of the filename. Add ':reverse' to reverse the
sort order
(e.g. <option>--pic-sort-by=mtime:reverse</option>).</para>
</listitem>
</varlistentry>
......@@ -341,8 +370,9 @@
</term>
<listitem>
<para>Sort order for subgalleries, among 'exif' (EXIF date of the
latest picture in sub-gallery), 'mtime' or 'dirname' (default is
'dirname'). Add ':reverse' to reverse the sort order
latest picture in sub-gallery), 'mtime', 'dirname', or 'numeric'
(default is 'dirname'). 'numeric' does a numeric sort on the
numeric part of the dirname. Add ':reverse' to reverse the sort order
(e.g. <option>--subgal-sort-by=dirname:reverse</option>).
</para>
</listitem>
......@@ -607,7 +637,8 @@ Album image identifier relative/path/to/image.jpg
<varlistentry>
<term><filename><parameter>SOURCE_DIR</parameter>/album-picture</filename></term>
<listitem>
<para>The image to use at the top of the album picture stack.</para>
<para>The relative path to the image to use at the top of the
album picture stack.</para>
</listitem>
</varlistentry>
......@@ -667,7 +698,7 @@ Album image identifier relative/path/to/image.jpg
<para>Multiple configuration files are processed by &dhpackage;.
The configuration is initially set up with the defaults. The defaults
can be found in the &dhpackage; source distribution in
<filename>lazygal/defaults.conf</filename>.</para>
<filename>lazygal/defaults.json</filename>.</para>
<para>Then, the configuration files are processed in the following order,
each newly defined value overloading formerly defined values.</para>
......
......@@ -38,22 +38,51 @@
</refnamediv>
<refsect1>
<title>DESCRIPTION</title>
<title>FORMAT DESCRIPTION</title>
<para>The configuration file is an INI like file
which configures <literal>lazygal</literal>. The format looks like this:
</para>
<para>&lazygal; is configured using JSON files. The format looks like
this:</para>
<informalexample><programlisting>
{
"sectionname": {
"variable" : "string value ",
"boolean" : false,
"list" : ["foo", "bar"],
"dictionnary" : {
"key1": "value1",
"key2": "value2"
}
},
"othersection": {
"foo" : "bar"
}
}
</programlisting></informalexample>
<para>This format is the preferred way to configure &lazygal;.</para>
</refsect1>
<refsect1>
<title>LEGACY FORMAT DESCRIPTION (INI)</title>
<para>The configuration file can also be an INI like file. The format
looks like this:</para>
<informalexample><programlisting>
[sectionname]
variable = value
othervariable = othervalue
variable = string value
boolean = Yes
list = foo, bar
dictionnary = key1=value1, key2=value2
[othersection]
foo = bar
</programlisting></informalexample>
<para>Boolean values can be conveniently set in the following ways:</para>
<para>In this INI format, boolean values can be conveniently set in the
following ways:</para>
<itemizedlist>
<listitem>
<para>For <literal>True</literal>:
......@@ -144,6 +173,22 @@ foo = bar
</listitem>
</varlistentry>
<varlistentry>
<term>preserve</term>
<listitem>
<para>Same as <option>--preserve=<replaceable>PATTERN</replaceable></option>
in &lazygal;. Multiple values may be separated by commas.</para>
</listitem>
</varlistentry>
<varlistentry>
<term>exclude</term>
<listitem>
<para>Same as <option>--exclude=<replaceable>PATTERN</replaceable></option>
in &lazygal;. Multiple values may be separated by commas.</para>
</listitem>
</varlistentry>
<varlistentry>
<term>dir-flattening-depth</term>
<listitem>
......@@ -359,8 +404,11 @@ foo = bar
with this configuration file:</para>
<programlisting>
[template-vars]
footer = &lt;p&gt;All pics are copyright 2011 me&lt;/p&gt;
{
"template-vars": {
"footer": "&lt;p&gt;All pics are copyright 2011 me&lt;/p&gt;"
}
}
</programlisting>
</example>
......
#!/usr/bin/env python
#!/usr/bin/env python3
#
# Lazygal, a lazy static web gallery generator.
# Copyright (C) 2007-2012 Alexandre Rossi <alexandre.rossi@gmail.com>
......@@ -86,6 +86,9 @@ parser.add_option("", "--clean-destination",
action="store_true",
dest="clean_destination",
help=_("Clean destination directory of files that should not be there."))
parser.add_option("", "--preserve", type="string",
action="append", metavar=_('PATTERN'),
dest="preserve", help=_("Specifies pathname(s) which will be ignored during final cleanup"))
parser.add_option("-v", "--version",
action="store_true",
dest="show_version",
......@@ -149,13 +152,16 @@ parser.add_option("", "--webalbum-pic-type",
help=_("Webalbum picture type. Default is messy."))
parser.add_option("", "--pic-sort-by",
action="store", metavar=_('ORDER'),
dest="pic_sort_by", help=_("Sort order for images in a folder: filename, mtime, or exif. Add ':reverse' to reverse the chosen order."))
dest="pic_sort_by", help=_("Sort order for images in a folder: filename, numeric, mtime, or exif. Add ':reverse' to reverse the chosen order."))
parser.add_option("", "--subgal-sort-by",
action="store", metavar=_('ORDER'),
dest="subgal_sort_by", help=_("Sort order for sub galleries in a folder: dirname, exif or mtime. Add ':reverse' to reverse the chosen order."))
dest="subgal_sort_by", help=_("Sort order for sub galleries in a folder: dirname, numeric, exif or mtime. Add ':reverse' to reverse the chosen order."))
parser.add_option("", "--filter-by-tag", type="string",
action="append", metavar=_('TAG'),
dest="filter_by_tag", help=_("Only include in the gallery pics whose IPTC keywords match the supplied filter(s)."))
parser.add_option("", "--exclude", type="string",
action="append", metavar=_('PATTERN'),
dest="exclude", help=_("Regular expression pattern(s) describing directories or filenames to exclude from consideration."))
parser.add_option("", "--keep-gps-data",
action="store_true",
dest="keep_gps",
......@@ -176,24 +182,26 @@ if not os.path.isdir(source_dir):
sys.exit(1)
cmdline_config = lazygal.config.BetterConfigParser()
for section in lazygal.config.DEFAULT_CONFIG.sections():
cmdline_config = lazygal.config.LazygalConfig()
for section in cmdline_config.valid_sections:
cmdline_config.add_section(section)
if options.quiet: cmdline_config.set('runtime', 'quiet', 'Yes')
if options.debug: cmdline_config.set('runtime', 'debug', 'Yes')
if options.quiet: cmdline_config.set('runtime', 'quiet', True)
if options.debug: cmdline_config.set('runtime', 'debug', True)
if options.check_all_dirs:
cmdline_config.set('runtime', 'check-all-dirs', 'Yes')
cmdline_config.set('runtime', 'check-all-dirs', True)
if options.dest_dir is not None:
cmdline_config.set('global', 'output-directory',
py2compat.u(options.dest_dir,
sys.getfilesystemencoding()))
if options.force_gen_pages:
cmdline_config.set('global', 'force-gen-pages', 'Yes')
cmdline_config.set('global', 'force-gen-pages', True)
if options.clean_destination:
cmdline_config.set('global', 'clean-destination', 'Yes')
cmdline_config.set('global', 'clean-destination', True)
if options.preserve is not None:
cmdline_config.set('global', 'preserve_args', options.preserve)
if options.dir_flattening_depth is not None:
cmdline_config.set('global', 'dir-flattening-depth',
options.dir_flattening_depth)
......@@ -201,6 +209,8 @@ if options.puburl is not None:
cmdline_config.set('global', 'puburl', options.puburl)
if options.theme is not None:
cmdline_config.set('global', 'theme', options.theme)
if options.exclude is not None:
cmdline_config.set('global', 'exclude_args', options.exclude)
if options.default_style is not None:
cmdline_config.set('webgal', 'default-style', options.default_style)
......@@ -231,13 +241,13 @@ if options.orig_symlink:
print(_("Option --orig-symlink is not available on this platform."))
sys.exit(1)
else:
cmdline_config.set('webgal', 'original-symlink', 'Yes')
cmdline_config.set('webgal', 'original-symlink', True)
if options.dirzip:
cmdline_config.set('webgal', 'dirzip', 'Yes')
cmdline_config.set('webgal', 'dirzip', True)
if options.quality is not None:
cmdline_config.set('webgal', 'jpeg-quality', options.quality)
if options.keep_gps:
cmdline_config.set('webgal', 'keep-gps', 'Yes')
cmdline_config.set('webgal', 'keep-gps', True)
if options.tpl_vars is not None:
cmdline_config.add_section('template-vars')
......
......@@ -15,6 +15,8 @@
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
import sys
import os
......@@ -35,30 +37,38 @@ else:
def get_hg_rev():
try:
lazygal_dir = os.path.join(os.path.dirname(__file__), '..')
if not os.path.isdir(os.path.join(lazygal_dir, '.hg')):
raise IOError
import mercurial.hg
import mercurial.node
import mercurial.ui
repo = mercurial.hg.repository(mercurial.ui.ui(), lazygal_dir)
last_revs = repo.changelog.parents(repo.dirstate.parents()[0])
known_tags = repo.tags().items()
for tag, rev in known_tags:
if tag != 'tip':
for last_rev in last_revs:
if rev == last_rev:
# This is a tagged revision, assume this is a release.
return ''
return mercurial.node.short(last_revs[0])
except (IOError, OSError, ImportError):
hgdir = os.path.join(os.path.dirname(__file__), '..', '.hg')
if os.path.isdir(hgdir):
import subprocess
last_revision_cache = os.path.join(hgdir, 'cache', 'last_revision')
if os.path.isfile(last_revision_cache)\
and (os.path.getmtime(last_revision_cache) >\
os.path.getmtime(os.path.join(hgdir, 'store', '00changelog.i'))):
with open(last_revision_cache, 'r') as fp:
return fp.read()
else:
try:
o = subprocess.check_output(('hg', 'log',
'--repository', os.path.join(hgdir, '..'),
'-T', '{node|short},{tags}',
'--rev', '-2'))
except subprocess.CalledProcessError:
os.unlink(last_revision_cache)
return ''
else:
rev, tag = o.decode(sys.getdefaultencoding()).split(',')
if tag:
# This is a tagged revision, thus a release
return ''
else:
with open(last_revision_cache, 'w') as fp:
fp.write(rev)
return rev
else:
return ''
__version__ = '0.8.8'
__version__ = '0.9'
hg_rev = get_hg_rev()
if hg_rev: __version__ += '+hg' + hg_rev
......
......@@ -22,6 +22,19 @@ try:
import configparser
except ImportError: # py2compat
import ConfigParser as configparser
configparser.RawConfigParser.read_file = configparser.RawConfigParser.readfp
import collections
import functools
import json
import copy
from . import py2compat
USER_CONFIG_PATH = os.path.expanduser('~/.lazygal/config')
DEFAULT_CONFIG_PATH = os.path.join(os.path.dirname(__file__),
'defaults.json')
class BetterConfigParser(configparser.RawConfigParser):
......@@ -90,22 +103,12 @@ class BetterConfigParser(configparser.RawConfigParser):
pass
USER_CONFIG_PATH = os.path.expanduser('~/.lazygal/config')
DEFAULT_CONFIG = BetterConfigParser()
DEFAULT_CONFIG.readfp(open(os.path.join(os.path.dirname(__file__),
'defaults.conf')))
class LazygalConfigDeprecated(BaseException): pass
class NoSectionError(BaseException): pass
class NoOptionError(BaseException): pass
class LazygalConfig(BetterConfigParser):
def __init__(self):
BetterConfigParser.__init__(self)
self.load(DEFAULT_CONFIG, init=True)
class LazygalIniConfig(BetterConfigParser):
def check_deprecation(self, config=None):
if config is None: config = self
......@@ -133,15 +136,188 @@ class LazygalConfig(BetterConfigParser):
option, section)
class LazygalWebgalConfig(LazygalConfig):
def false_or(s, f=None):
if s.lower() in ('no', 'false'):
return False
elif f:
return f(s)
else:
return s
def get_bool(s):
if s.lower() in ('true', 'yes', 'on', '1') or s == 1:
return True
if s.lower() in ('false', 'no', 'off', '0') or s == 0:
return False
raise ValueError(_("Unknown boolean value '%s'") % s)
def get_list(s):
if s:
return [i.strip() for i in s.split(',')]
return []
def get_dict(s):
d = {}
for i in s.split(','):
key, value = i.split('=')
d[key.strip()] = value.strip()
return d
def get_int(s):
return int(s)
def get_order(s):
try:
order, reverse = s.split(':')
except ValueError:
order, reverse = s, False
return {'order': order, 'reverse': reverse == 'reverse'}
STRING_TO_JSON = collections.OrderedDict({
'runtime': {
'quiet' : get_bool,
'debug' : get_bool,
'check-all-dirs' : get_bool,
},
'global': {
'force-gen-pages' : get_bool,
'clean-destination' : get_bool,
'preserve' : get_list,
'dir-flattening-depth': functools.partial(false_or, f=get_int),
'puburl' : false_or,
'exclude' : get_list,
'preserve_args' : get_list,
'exclude_args' : get_list,
},
'webgal': {
'image-size' : get_dict,
'thumbs-per-page' : get_int,
'filter-by-tag' : get_list,
'sort-medias' : get_order,
'sort-subgals' : get_order,
'original' : get_bool,
'original-baseurl' : false_or,
'original-symlink' : get_bool,
'dirzip' : get_bool,
'jpeg-quality' : get_int,
'jpeg-optimize' : get_bool,
'jpeg-progressive' : get_bool,
'publish-metadata' : get_bool,
'keep-gps' : get_bool,
},
})
class LazygalConfig(object):
valid_sections = ('runtime', 'global', 'webgal', 'template-vars', )
def __init__(self, load_defaults=False):
self.c = collections.OrderedDict()
self.valid_options = {s:[] for s in self.valid_sections}
self.load(self.load_file(DEFAULT_CONFIG_PATH),
defaults=True, setvalue=load_defaults)
def has_section(self, section):
return section in self.c
def add_section(self, section):
if not self.has_section(section):
if section in self.valid_sections:
self.c[section] = collections.OrderedDict()
else:
raise ValueError("section '%s' is not a valid section name"
% section)
def options(self, section):
return self.c[section].keys()
def get(self, section, option):
if not self.has_section(section):
raise NoSectionError(section)
if option in self.c[section]:
return self.c[section][option]
else:
raise NoOptionError(_('%s in section %s') % (option, section))
def set(self, section, option, value):
self.add_section(section)
if option in self.valid_options[section] or section == 'template-vars':
if py2compat.isstr(value)\
and section in STRING_TO_JSON\
and option in STRING_TO_JSON[section]:
value = STRING_TO_JSON[section][option](value)
self.c[section][option] = copy.deepcopy(value)
else:
raise ValueError(_("option '%s' is not valid in section '%s'")
% (option, section))
def load(self, newconf, setvalue=True, defaults=False):
for section, s in newconf.items():
if defaults and section not in self.valid_sections:
continue # ignore default section loading
for k, v in s.items():
if defaults:
self.valid_options[section].append(k)
if not defaults or (defaults and setvalue):
try:
self.set(section, k, v)
except ValueError as e:
logging.warning(_('Ignoring option: %s') % e.args[0])
def load_file(self, path):
newconf = None
with open(path, 'r') as json_fp:
newconf = json.load(json_fp)
return newconf
def load_inifile(self, path):
iniconf = LazygalIniConfig(defaults=False)
with open(path) as fp:
iniconf.read_file(fp)
for s in iniconf.sections():
for key, value in iniconf.items(s):
try:
self.set(s, key, value)
except ValueError as e:
logging.warning(_('Ignoring option: %s') % e.args[0])
def load_any(self, path):
if not os.path.isfile(path):
logging.debug(_('Cannot load non-existent config %s.') % path)
return
try:
self.load(self.load_file(path))
except ValueError:
try:
self.load_inifile(path)
except Exception:
raise
else:
logging.warning(_('INI-style config file format is deprecated.'))
def __str__(self):
return json.dumps(self.c)
def items(self):
return self.c.items()
def __init__(self, global_config):
LazygalConfig.__init__(self)
LazygalConfig.load(self, global_config, init=True)
def __getitem__(self, key):
return self.c[key]
def load(self, other_config, init=False):
LazygalConfig.load(self, other_config, init,
sections=('webgal', 'template-vars', ))
class LazygalWebgalConfig(LazygalConfig):
valid_sections = ('webgal', 'template-vars', )
# vim: ts=4 sw=4 expandtab
[runtime]
quiet = No
debug = No
check-all-dirs = No
[global]
output-directory = .
force-gen-pages = No
clean-destination = No
dir-flattening-depth = No
puburl = No
theme = default
[webgal]
default-style = default
webalbumpic-bg = transparent
webalbumpic-type = messy
webalbumpic-size = 200x150
image-size = small=800x600,medium=1024x768
thumbnail-size = 150x113
video-size = 0x0
thumbs-per-page = 0
filter-by-tag =
sort-medias = exif
sort-subgals = dirname
original = No
original-baseurl = No
original-symlink = No
dirzip = No
jpeg-quality = 85
jpeg-optimize = Yes
jpeg-progressive = Yes
publish-metadata = Yes
keep-gps = No