Skip to content

Commit

Permalink
update only a single album
Browse files Browse the repository at this point in the history
  • Loading branch information
dsschult committed Feb 20, 2024
1 parent 27e10d4 commit 34bb74a
Show file tree
Hide file tree
Showing 6 changed files with 144 additions and 77 deletions.
11 changes: 11 additions & 0 deletions docs/getting_started.rst
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,17 @@ Optional arguments:
My Pictures/Landscapes => Force
My Other Pictures/Landscapes => Force

``--only-album``
Only write a specific album path. Other albums will be ignored as if you
had set the ``ignore_directories`` setting to all of their names.

::

--only-album 'My Pictures/Pics'
My Pictures/Pics => Write
My Pictures/Pics/* => Ignore
* => Ignore

``-v, --verbose``
Show all messages

Expand Down
12 changes: 11 additions & 1 deletion src/sigal/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,15 @@ def init(path):
"the album name. (-a 'My Pictures/* Pics' -a 'Festival')"
),
)
@option(
"--only-album",
default=None,
help=(
"Only write a specific album path. Other albums will be ignored "
"as if you had set the `ignore_directories` setting to all of "
"their names. (--only-album 'My Pictures/Pics')"
),
)
@option("-v", "--verbose", is_flag=True, help="Show all messages")
@option(
"-d",
Expand Down Expand Up @@ -120,6 +129,7 @@ def build(
quiet,
force,
force_album,
only_album,
config,
theme,
title,
Expand Down Expand Up @@ -186,7 +196,7 @@ def build(
locale.setlocale(locale.LC_ALL, settings["locale"])
init_plugins(settings)

gal = Gallery(settings, ncpu=ncpu, show_progress=show_progress)
gal = Gallery(settings, ncpu=ncpu, show_progress=show_progress, only_album=only_album)
gal.build(force=force_album if len(force_album) else force)

# copy extra files
Expand Down
74 changes: 50 additions & 24 deletions src/sigal/gallery.py
Original file line number Diff line number Diff line change
Expand Up @@ -699,7 +699,7 @@ def zip(self):


class Gallery:
def __init__(self, settings, ncpu=None, show_progress=False):
def __init__(self, settings, ncpu=None, show_progress=False, only_album=None):
self.settings = settings
self.logger = logging.getLogger(__name__)
self.stats = defaultdict(int)
Expand All @@ -711,6 +711,7 @@ def __init__(self, settings, ncpu=None, show_progress=False):

# Build the list of directories with images
albums = self.albums = {}
self.only_album = only_album
src_path = self.settings["source"]

ignore_dirs = settings["ignore_directories"]
Expand All @@ -724,17 +725,26 @@ def __init__(self, settings, ncpu=None, show_progress=False):

self.progressbar_target = None if show_progress and isatty else Devnull()

for path, dirs, files in os.walk(src_path, followlinks=True, topdown=False):
for path, dirs, files in os.walk(src_path, followlinks=True, topdown=True):
if show_progress:
print("\rCollecting albums " + next(progressChars), end="")
relpath = os.path.relpath(path, src_path)

# Test if the directory match the ignore_dirs settings
if ignore_dirs and any(
fnmatch.fnmatch(relpath, ignore) for ignore in ignore_dirs
):
self.logger.info("Ignoring %s", relpath)
continue
for d in dirs[:]:
dir_relpath = join(relpath, d) if relpath != "." else d

# Test if the directory matches the only_album settings
if only_album and dir_relpath not in only_album and relpath != only_album:
self.logger.info("Skipping %s", dir_relpath)
dirs.remove(d)
continue

# Test if the directory matches the ignore_dirs settings
if ignore_dirs and any(
fnmatch.fnmatch(dir_relpath, ignore) for ignore in ignore_dirs
):
self.logger.info("Ignoring %s", dir_relpath)
dirs.remove(d)

# Remove files that match the ignore_files settings
if ignore_files:
Expand All @@ -746,21 +756,29 @@ def __init__(self, settings, ncpu=None, show_progress=False):
files = [os.path.split(f)[1] for f in files_path]
self.logger.debug("Files after filtering: %r", files)

# Remove sub-directories that have been ignored in a previous
# iteration (as topdown=False, sub-directories are processed before
# their parent
for d in dirs[:]:
path = join(relpath, d) if relpath != "." else d
if path not in albums.keys():
dirs.remove(d)

album = Album(relpath, settings, dirs, files, self)

if not album.medias and not album.albums:
self.logger.info("Skip empty album: %r", album)
else:
album.create_output_directories()
albums[relpath] = album
self.logger.debug("Processing subdirs: %r", dirs)

self.stats["album"] += 1
albums[relpath] = album

# Remove empty albums
for relpath in sorted(albums.keys(), key=lambda x: len(x), reverse=True):
album = albums[relpath]
keep_only_album_children = only_album and os.path.relpath(os.path.dirname(album.src_path), src_path) == only_album
if not album.medias and not album.subdirs and not keep_only_album_children:
self.logger.info("Skip empty album: %r", album.path)
del albums[relpath]
if relpath != '.':
super_album = os.path.relpath(os.path.dirname(album.src_path), src_path)
self.logger.debug("Deleting album from super album %s", super_album)
albums[super_album].subdirs.remove(os.path.basename(album.path))
self.stats["album_skipped"] += 1
self.stats["album"] -= self.stats["album_skipped"]

for album in albums.values():
album.create_output_directories()

if show_progress:
print("\rCollecting albums, done.")
Expand All @@ -771,6 +789,8 @@ def __init__(self, settings, ncpu=None, show_progress=False):
file=self.progressbar_target,
) as progress_albums:
for album in progress_albums:
if only_album and album.path != only_album:
continue
album.sort_subdirs(settings["albums_sort_attr"])

with progressbar(
Expand All @@ -779,6 +799,8 @@ def __init__(self, settings, ncpu=None, show_progress=False):
file=self.progressbar_target,
) as progress_albums:
for album in progress_albums:
if only_album and album.path != only_album:
continue
album.sort_medias(settings["medias_sort_attr"])

self.logger.debug("Albums:\n%r", albums.values())
Expand Down Expand Up @@ -846,9 +868,11 @@ def log_func(x):
show_eta=False,
file=self.progressbar_target,
) as albums:
media_list = [
f for album in albums for f in self.process_dir(album, force=force)
]
media_list = []
for album in albums:
if self.only_album and album.path != self.only_album:
continue
media_list.extend(self.process_dir(album, force=force))
except KeyboardInterrupt:
sys.exit("Interrupted")

Expand Down Expand Up @@ -902,6 +926,8 @@ def log_func(x):
file=self.progressbar_target,
) as albums:
for album in albums:
if self.only_album and album.path != self.only_album:
continue
if album.albums:
if album.medias:
self.logger.warning(
Expand Down
60 changes: 8 additions & 52 deletions src/sigal/plugins/nomedia.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,31 +54,6 @@
logger = logging.getLogger(__name__)


def _remove_albums_with_subdirs(albums, keystoremove, prefix=""):
for keytoremove in keystoremove:
for key in list(albums.keys()):
if key.startswith(prefix + keytoremove):
# subdirs' target directories have already been created,
# remove them first
try:
album = albums[key]
settings = album.settings
if album.medias:
os.rmdir(os.path.join(album.dst_path, settings["thumb_dir"]))

if album.medias and settings["keep_orig"]:
os.rmdir(os.path.join(album.dst_path, settings["orig_dir"]))

os.rmdir(album.dst_path)
except OSError:
# directory was created and populated with images in a
# previous run => keep it
pass

# now remove the album from the surrounding album/gallery
del albums[key]


def filter_nomedia(album, settings=None):
"""Removes all filtered Media and subdirs from an Album"""
nomediapath = os.path.join(album.src_path, ".nomedia")
Expand All @@ -90,39 +65,20 @@ def filter_nomedia(album, settings=None):
logger.info(
"Ignoring album '%s' because of present 0-byte .nomedia file", album.name
)

# subdirs have been added to the gallery already, remove them
# there, too
_remove_albums_with_subdirs(album.gallery.albums, [album.path])
try:
os.rmdir(album.dst_path)
except OSError:
# directory was created and populated with images in a
# previous run => keep it
pass

# cannot set albums => empty subdirs so that no albums are
# generated
album.subdirs = []
album.medias = []
album.subdirs.clear()
album.medias.clear()

else:
with open(nomediapath) as nomediaFile:
logger.info("Found a .nomedia file in %s, ignoring its entries", album.name)
ignored = nomediaFile.read().split("\n")

album.medias = [
media for media in album.medias if media.src_filename not in ignored
]
album.subdirs = [
dirname for dirname in album.subdirs if dirname not in ignored
]

# subdirs have been added to the gallery already, remove
# them there, too
_remove_albums_with_subdirs(
album.gallery.albums, ignored, album.path + os.path.sep
)
for media in album.medias[:]:
if media.src_filename in ignored:
album.medias.remove(media)
for dirname in album.subdirs[:]:
if dirname in ignored:
album.subdirs.remove(dirname)


def register(settings):
Expand Down
49 changes: 49 additions & 0 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,55 @@ def test_build(tmpdir, disconnect_signals):
logger.setLevel(logging.INFO)


def test_build_only_album(tmpdir, disconnect_signals):
runner = CliRunner()
config_file = str(tmpdir.join("sigal.conf.py"))
tmpdir.mkdir("pictures")
tmpdir = str(tmpdir)
cwd = os.getcwd()

try:
result = runner.invoke(init, [config_file])
assert result.exit_code == 0
os.symlink(
join(TESTGAL, "pictures", "dir2"),
join(tmpdir, "pictures", "dir1"),
)

os.chdir(tmpdir)

with open(config_file) as f:
text = f.read()

text += """
theme = 'colorbox'
plugins = ['sigal.plugins.media_page', 'sigal.plugins.nomedia',
'sigal.plugins.extended_caching']
"""

with open(config_file, "w") as f:
f.write(text)

result = runner.invoke(
build,
["pictures", "build", "--title", "Testing build", "-n", 1, "--debug", "--only-album", "dir1"],
catch_exceptions=False,
)
assert result.exit_code == 0
assert os.path.isfile(
join(tmpdir, "build", "dir1", "index.html")
)
assert not os.path.isfile(
join(tmpdir, "build", "dir2", "index.html")
)
finally:
os.chdir(cwd)
# Reset logger
logger = logging.getLogger("sigal")
logger.handlers[:] = []
logger.setLevel(logging.INFO)


def test_serve(tmpdir):
config_file = str(tmpdir.join("sigal.conf.py"))
runner = CliRunner()
Expand Down
15 changes: 15 additions & 0 deletions tests/test_gallery.py
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,21 @@ def test_gallery(settings, tmp_path, caplog):
logger.setLevel(logging.INFO)


def test_gallery_only_album(settings, tmp_path, caplog):
"Test the Gallery class updating a single album."

caplog.set_level("ERROR")
settings["destination"] = str(tmp_path)
gal = Gallery(settings, ncpu=1, only_album="dir1")
gal.build()

out_html = os.path.join(settings["destination"], "dir1", "index.html")
assert os.path.isfile(out_html)

out_html2 = os.path.join(settings["destination"], "dir2", "index.html")
assert not os.path.isfile(out_html2)


def test_custom_theme(settings, tmp_path, caplog):
theme_path = tmp_path / "mytheme"
tpl_path = theme_path / "templates"
Expand Down

0 comments on commit 34bb74a

Please sign in to comment.