sigal 2.5__py3-none-any.whl → 2.6__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
sigal/__init__.py CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2009-2023 - Simon Conseil
1
+ # Copyright (c) 2009-2026 - Simon Conseil
2
2
 
3
3
  # Permission is hereby granted, free of charge, to any person obtaining a copy
4
4
  # of this software and associated documentation files (the "Software"), to
sigal/__main__.py CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2009-2023 - Simon Conseil
1
+ # Copyright (c) 2009-2026 - Simon Conseil
2
2
 
3
3
  # Permission is hereby granted, free of charge, to any person obtaining a copy
4
4
  # of this software and associated documentation files (the "Software"), to
@@ -237,15 +237,17 @@ def serve(destination, port, config, browser):
237
237
  settings = read_settings(config)
238
238
  destination = settings.get("destination")
239
239
  if not os.path.exists(destination):
240
- sys.stderr.write(
240
+ click.echo(
241
241
  f"The '{destination}' directory doesn't exist, maybe try building"
242
- " first?\n"
242
+ " first?",
243
+ err=True,
243
244
  )
244
245
  sys.exit(1)
245
246
  else:
246
- sys.stderr.write(
247
+ click.echo(
247
248
  f"The {destination} directory doesn't exist "
248
- f"and the config file ({config}) could not be read.\n"
249
+ f"and the config file ({config}) could not be read.",
250
+ err=True,
249
251
  )
250
252
  sys.exit(2)
251
253
 
@@ -284,10 +286,10 @@ def set_meta(target, keys, overwrite=False):
284
286
  """
285
287
 
286
288
  if not os.path.exists(target):
287
- sys.stderr.write(f"The target {target} does not exist.\n")
289
+ click.echo(f"The target {target} does not exist.", err=True)
288
290
  sys.exit(1)
289
291
  if len(keys) < 2 or len(keys) % 2 > 0:
290
- sys.stderr.write("Need an even number of arguments.\n")
292
+ click.echo("Need an even number of arguments.", err=True)
291
293
  sys.exit(1)
292
294
 
293
295
  if os.path.isdir(target):
@@ -295,9 +297,10 @@ def set_meta(target, keys, overwrite=False):
295
297
  else:
296
298
  descfile = os.path.splitext(target)[0] + ".md"
297
299
  if os.path.exists(descfile) and not overwrite:
298
- sys.stderr.write(
300
+ click.echo(
299
301
  f"Description file '{descfile}' already exists. "
300
- "Use --overwrite to overwrite it.\n"
302
+ "Use --overwrite to overwrite it.",
303
+ err=True,
301
304
  )
302
305
  sys.exit(2)
303
306
 
sigal/gallery.py CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2009-2023 - Simon Conseil
1
+ # Copyright (c) 2009-2026 - Simon Conseil
2
2
  # Copyright (c) 2013 - Christophe-Marie Duquesne
3
3
  # Copyright (c) 2014 - Jonas Kaufmann
4
4
  # Copyright (c) 2015 - François D.
@@ -52,7 +52,7 @@ from .image import (
52
52
  get_size,
53
53
  process_image,
54
54
  )
55
- from .settings import Status, get_thumb
55
+ from .settings import IMG_EXTENSIONS, Status, get_thumb
56
56
  from .utils import (
57
57
  Devnull,
58
58
  check_or_create_dir,
@@ -246,15 +246,10 @@ class Image(Media):
246
246
  super().__init__(filename, path, settings)
247
247
  imgformat = settings.get("img_format")
248
248
 
249
- # Register all formats
250
- PILImage.init()
251
-
252
- if imgformat and PILImage.EXTENSION[self.src_ext] != imgformat.upper():
249
+ if imgformat and IMG_EXTENSIONS.ext2format[self.src_ext] != imgformat.upper():
253
250
  # Find the extension that should match img_format
254
- extensions = {v: k for k, v in PILImage.EXTENSION.items()}
255
- ext = extensions[imgformat.upper()]
251
+ ext = IMG_EXTENSIONS.format2ext[imgformat.upper()]
256
252
  self.dst_filename = self.basename + ext
257
- self.thumb_name = get_thumb(self.settings, self.dst_filename)
258
253
 
259
254
  @cached_property
260
255
  def date(self):
@@ -593,76 +588,76 @@ class Album:
593
588
  # Test the thumbnail from the Markdown file.
594
589
  thumbnail = self.meta.get("thumbnail", [""])[0]
595
590
 
596
- if thumbnail and isfile(join(self.src_path, thumbnail)):
597
- self._thumbnail = url_from_path(
598
- join(self.name, get_thumb(self.settings, thumbnail))
599
- )
591
+ if thumbnail:
592
+ # if thumbnail is set in the markdown, it can be either the
593
+ # original filename or the generated name after format conversion
594
+ if isfile(join(self.src_path, thumbnail)):
595
+ thumbnail = get_thumb(self.settings, thumbnail)
596
+ self._thumbnail = url_from_path(join(self.name, thumbnail))
600
597
  self.logger.debug("Thumbnail for %r : %s", self, self._thumbnail)
601
598
  return self._thumbnail
602
- else:
603
- # find and return the first landscape image
604
- for f in self.medias:
605
- ext = splitext(f.dst_filename)[1]
606
- if ext.lower() not in self.settings["img_extensions"]:
607
- continue
608
-
609
- # Use f.size if available as it is quicker (in cache), but
610
- # fallback to the size of src_path if dst_path is missing
611
- size = f.input_size
612
- if size is None:
613
- size = f.file_metadata["size"]
614
-
615
- if size["width"] > size["height"]:
599
+
600
+ # find and return the first landscape image
601
+ for f in self.medias:
602
+ ext = splitext(f.dst_filename)[1]
603
+ if ext.lower() not in self.settings["img_extensions"]:
604
+ continue
605
+
606
+ # Use f.size if available as it is quicker (in cache), but
607
+ # fallback to the size of src_path if dst_path is missing
608
+ size = f.input_size
609
+ if size is None:
610
+ size = f.file_metadata["size"]
611
+
612
+ if size["width"] > size["height"]:
613
+ try:
614
+ self._thumbnail = url_quote(self.name) + "/" + f.thumbnail
615
+ except Exception as e:
616
+ self.logger.info(
617
+ "Failed to get thumbnail for %s: %s", f.dst_filename, e
618
+ )
619
+ else:
620
+ self.logger.debug(
621
+ "Use 1st landscape image as thumbnail for %r : %s",
622
+ self,
623
+ self._thumbnail,
624
+ )
625
+ return self._thumbnail
626
+
627
+ # else simply return the 1st media file
628
+ if not self._thumbnail and self.medias:
629
+ for media in self.medias:
630
+ if media.thumbnail is not None:
616
631
  try:
617
- self._thumbnail = url_quote(self.name) + "/" + f.thumbnail
632
+ self._thumbnail = url_quote(self.name) + "/" + media.thumbnail
618
633
  except Exception as e:
619
634
  self.logger.info(
620
- "Failed to get thumbnail for %s: %s", f.dst_filename, e
635
+ "Failed to get thumbnail for %s: %s",
636
+ media.dst_filename,
637
+ e,
621
638
  )
622
639
  else:
623
- self.logger.debug(
624
- "Use 1st landscape image as thumbnail for %r : %s",
625
- self,
626
- self._thumbnail,
627
- )
628
- return self._thumbnail
629
-
630
- # else simply return the 1st media file
631
- if not self._thumbnail and self.medias:
632
- for media in self.medias:
633
- if media.thumbnail is not None:
634
- try:
635
- self._thumbnail = (
636
- url_quote(self.name) + "/" + media.thumbnail
637
- )
638
- except Exception as e:
639
- self.logger.info(
640
- "Failed to get thumbnail for %s: %s",
641
- media.dst_filename,
642
- e,
643
- )
644
- else:
645
- break
646
- else:
647
- self.logger.warning("No thumbnail found for %r", self)
648
- return
640
+ break
641
+ else:
642
+ self.logger.warning("No thumbnail found for %r", self)
643
+ return
649
644
 
650
- self.logger.debug(
651
- "Use the 1st image as thumbnail for %r : %s", self, self._thumbnail
652
- )
653
- return self._thumbnail
654
-
655
- # use the thumbnail of their sub-directories
656
- if not self._thumbnail:
657
- for path, album in self.gallery.get_albums(self.path):
658
- if album.thumbnail:
659
- self._thumbnail = url_quote(self.name) + "/" + album.thumbnail
660
- self.logger.debug(
661
- "Using thumbnail from sub-directory for %r : %s",
662
- self,
663
- self._thumbnail,
664
- )
665
- return self._thumbnail
645
+ self.logger.debug(
646
+ "Use the 1st image as thumbnail for %r : %s", self, self._thumbnail
647
+ )
648
+ return self._thumbnail
649
+
650
+ # use the thumbnail of their sub-directories
651
+ if not self._thumbnail:
652
+ for path, album in self.gallery.get_albums(self.path):
653
+ if album.thumbnail:
654
+ self._thumbnail = url_quote(self.name) + "/" + album.thumbnail
655
+ self.logger.debug(
656
+ "Using thumbnail from sub-directory for %r : %s",
657
+ self,
658
+ self._thumbnail,
659
+ )
660
+ return self._thumbnail
666
661
 
667
662
  self.logger.error("Thumbnail not found for %r", self)
668
663
 
@@ -918,14 +913,6 @@ class Gallery:
918
913
  ) as albums:
919
914
  for album in albums:
920
915
  if album.albums:
921
- if album.medias:
922
- self.logger.warning(
923
- "Album '%s' contains sub-albums and images. "
924
- "Please move images to their own sub-album. "
925
- "Images in album %s will not be visible.",
926
- album.title,
927
- album.title,
928
- )
929
916
  album_list_writer.write(album)
930
917
  else:
931
918
  album_writer.write(album)
sigal/image.py CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2009-2023 - Simon Conseil
1
+ # Copyright (c) 2009-2026 - Simon Conseil
2
2
  # Copyright (c) 2015 - François D.
3
3
  # Copyright (c) 2018 - Edwin Steele
4
4
 
@@ -99,6 +99,7 @@ def generate_image(source, outname, settings, options=None):
99
99
 
100
100
  img = _read_image(source)
101
101
  original_format = img.format
102
+ logger.debug("Read %s: %dx%d (%s)", source, *img.size, original_format)
102
103
 
103
104
  if settings["copy_exif_data"] and settings["autorotate_images"]:
104
105
  logger.warning(
@@ -149,7 +150,7 @@ def generate_image(source, outname, settings, options=None):
149
150
  # format, or fall back to JPEG
150
151
  outformat = settings.get("img_format") or img.format or original_format or "JPEG"
151
152
 
152
- logger.debug("Save resized image to %s (%s)", outname, outformat)
153
+ logger.debug("Save resized image: %s, %dx%d (%s)", outname, *img.size, outformat)
153
154
  save_image(img, outname, outformat, options=options, autoconvert=True)
154
155
 
155
156
 
@@ -162,6 +163,7 @@ def generate_thumbnail(
162
163
  img = _read_image(source)
163
164
  img = Transpose().process(img)
164
165
  original_format = img.format
166
+ logger.debug("Read %s: %dx%d (%s)", source, *img.size, original_format)
165
167
 
166
168
  try:
167
169
  method = PILImage.Resampling.LANCZOS
@@ -175,7 +177,7 @@ def generate_thumbnail(
175
177
  img.thumbnail(box, method)
176
178
 
177
179
  outformat = img.format or original_format or "JPEG"
178
- logger.debug("Save thumnail image: %s (%s)", outname, outformat)
180
+ logger.debug("Save thumbnail image: %s, %dx%d (%s)", outname, *img.size, outformat)
179
181
  save_image(img, outname, outformat, options=options, autoconvert=True)
180
182
 
181
183
 
sigal/log.py CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2013-2023 - Simon Conseil
1
+ # Copyright (c) 2013-2026 - Simon Conseil
2
2
 
3
3
  # Permission is hereby granted, free of charge, to any person obtaining a copy
4
4
  # of this software and associated documentation files (the "Software"), to
sigal/plugins/feeds.py CHANGED
@@ -70,6 +70,7 @@ def generate_feed(gallery, medias, feed_type=None, feed_url="", nb_items=0):
70
70
  feed.add_item(
71
71
  title=Markup.escape(item.title or item.url),
72
72
  link=link,
73
+ content=None,
73
74
  # unique_id='tag:%s,%s:%s' % (urlparse(link).netloc,
74
75
  # item.date.date(),
75
76
  # urlparse(link).path.lstrip('/')),
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2009-2023 - Simon Conseil
1
+ # Copyright (c) 2009-2026 - Simon Conseil
2
2
  # Copyright (c) 2014 - Jamie Starke
3
3
 
4
4
  # Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -4,6 +4,9 @@ This plugin will copy the files into the build tree and generate generic
4
4
  thumbnails for the files. In-browser previews will likely fail, and
5
5
  it is up to the theme to provide correct support for downloads.
6
6
 
7
+ If the `pdf2image <https://pypi.org/project/pdf2image/>`_ optional dependency is installed,
8
+ it will be used to generate thumbnails for PDF files.
9
+
7
10
  Settings available as dictionary in ``nonmedia_files_options``:
8
11
 
9
12
  - ``ext_as_thumb``: Enable simple thumbnail showing ext. Default to ``True``
@@ -26,6 +29,10 @@ Settings available as dictionary in ``nonmedia_files_options``:
26
29
  import logging
27
30
  import os
28
31
 
32
+ try: # Optional dependency:
33
+ from pdf2image import convert_from_path as pdf2img
34
+ except ImportError:
35
+ pdf2img = None
29
36
  from PIL import Image as PILImage
30
37
  from PIL import ImageDraw, ImageFont
31
38
  from pilkit.utils import save_image
@@ -102,9 +109,7 @@ def generate_thumbnail(
102
109
  logger.info(f"kwargs: {kwargs}")
103
110
  d.text(anchor, text, anchor="mm", **kwargs)
104
111
 
105
- outformat = "JPEG"
106
- logger.info("Save thumnail image: %s (%s)", outname, outformat)
107
- save_image(img, outname, outformat, options=options, autoconvert=True)
112
+ save_image(img, outname, "JPEG", options=options, autoconvert=True)
108
113
 
109
114
 
110
115
  def process_thumb(media):
@@ -113,18 +118,24 @@ def process_thumb(media):
113
118
  utils.copy(media.src_path, media.dst_path, symlink=settings["orig_link"])
114
119
 
115
120
  if plugin_settings.get("ext_as_thumb", DEFAULT_CONFIG["ext_as_thumb"]):
116
- logger.info("plugin_settings: %r", plugin_settings)
117
- kwargs = {}
118
- for key in ("bg_color", "font", "font_color", "font_size"):
119
- if f"thumb_{key}" in plugin_settings:
120
- kwargs[key] = plugin_settings[f"thumb_{key}"]
121
- generate_thumbnail(
122
- media.src_ext[1:].upper(),
123
- media.thumb_path,
124
- settings["thumb_size"],
125
- options=settings["jpg_options"],
126
- **kwargs,
127
- )
121
+ if pdf2img and media.src_ext.lower() == ".pdf":
122
+ images = pdf2img(
123
+ media.src_path, single_file=True, size=settings["thumb_size"]
124
+ )
125
+ images[0].save(media.thumb_path)
126
+ else:
127
+ kwargs = {}
128
+ for key in ("bg_color", "font", "font_color", "font_size"):
129
+ if f"thumb_{key}" in plugin_settings:
130
+ kwargs[key] = plugin_settings[f"thumb_{key}"]
131
+ generate_thumbnail(
132
+ media.src_ext[1:].upper(),
133
+ media.thumb_path,
134
+ settings["thumb_size"],
135
+ options=settings["jpg_options"],
136
+ **kwargs,
137
+ )
138
+ logger.info("Saved thumbnail image: %s", media.thumb_path)
128
139
 
129
140
 
130
141
  def process_nonmedia(media):
sigal/settings.py CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2009-2023 - Simon Conseil
1
+ # Copyright (c) 2009-2026 - Simon Conseil
2
2
  # Copyright (c) 2013 - Christophe-Marie Duquesne
3
3
  # Copyright (c) 2017 - Mate Lakat
4
4
  # Copyright (c) 2021 - Keith Feldman
@@ -26,6 +26,8 @@ import os
26
26
  from os.path import abspath, isabs, join, normpath
27
27
  from pprint import pformat
28
28
 
29
+ from PIL import Image as PILImage
30
+
29
31
  _DEFAULT_CONFIG = {
30
32
  "albums_sort_attr": "name",
31
33
  "albums_sort_reverse": False,
@@ -112,6 +114,20 @@ class Status:
112
114
  FAILURE = 1
113
115
 
114
116
 
117
+ class _ImgExtensions:
118
+ def __init__(self):
119
+ # Register all formats
120
+ PILImage.init()
121
+
122
+ self.ext2format = PILImage.EXTENSION
123
+ self.format2ext = {v: k for k, v in PILImage.EXTENSION.items()}
124
+ self.format2ext["JPEG"] = ".jpg" # prefered ext for jpg
125
+ self.format2ext["PNG"] = ".png" # prefered ext for png
126
+
127
+
128
+ IMG_EXTENSIONS = _ImgExtensions()
129
+
130
+
115
131
  def get_thumb(settings, filename):
116
132
  """Return the path to the thumb.
117
133
 
@@ -132,6 +148,11 @@ def get_thumb(settings, filename):
132
148
 
133
149
  if ext.lower() in settings["video_extensions"]:
134
150
  ext = ".jpg"
151
+
152
+ imgformat = settings.get("img_format")
153
+ if imgformat:
154
+ ext = IMG_EXTENSIONS.format2ext[imgformat]
155
+
135
156
  return join(
136
157
  path,
137
158
  settings["thumb_dir"],
@@ -173,7 +194,7 @@ def read_settings(filename=None):
173
194
  settings[p] = abspath(normpath(join(settings_path, path)))
174
195
  logger.debug("Rewrite %s : %s -> %s", p, path, settings[p])
175
196
 
176
- for key in ("img_size", "thumb_size", "video_size"):
197
+ for key in ("img_size", "video_size"):
177
198
  if settings[key]:
178
199
  w, h = settings[key]
179
200
  if h > w:
@@ -16,47 +16,49 @@
16
16
 
17
17
  {% include 'map.html' %}
18
18
  <div id="gallery">
19
- {% for media in album.medias | selectattr("type", "in", ("image", "video")) %}
19
+ {% for media in album.medias %}
20
20
  {% if loop.index % nb_columns == 1 %}
21
- <div id="albums" class="row">
21
+ <div id="albums" class="row">
22
22
  {% endif%}
23
- {% if media.type == "image" %}
23
+ {% if media.thumbnail %}
24
24
  <div class="{{ column_size_t }} columns thumbnail">
25
+ {% if media.type == "image" %}
26
+ <a class="gallery" title="{{ media.title }}" {{ img_description(media) }}
27
+ {% if 'sigal.plugins.media_page' in settings.plugins %}
28
+ href="{{ media.url }}.html" data-href="{{ media.url }}"
29
+ {% else %}
30
+ href="{{ media.url }}"
31
+ {% endif %}
32
+ {% elif media.type == "video" %}
33
+ {% set mhash = media.url|replace('.', '')|replace(' ', '') %}
25
34
  {% if 'sigal.plugins.media_page' in settings.plugins %}
26
- <a href="{{ media.url}}.html" class="gallery"
27
- title="{{ media.title }}"
28
- data-href="{{ media.url }}" {{ img_description(media) }}>
35
+ <a href="{{ media.url }}.html" data-href="#{{ mhash }}"
29
36
  {% else %}
30
- <a href="{{ media.url }}" class="gallery" title="{{ media.title }}"
31
- {{ img_description(media) }}>
37
+ <a href="#{{ mhash }}" class="gallery" inline='yes' title="{{ media.url }}"
32
38
  {% endif %}
39
+ {% if media.big %} data-big="{{ media.big_url }}"{% endif %}
40
+ {% else %}
41
+ <a href="{{ media.url }}" title="{{ media.title }}"
42
+ {% endif %}
43
+ >
33
44
  <img src="{{ media.thumbnail }}" alt="{{ media.url }}"
34
- title="{{ media.title }}" /></a>
45
+ title="{{ media.title }}">
46
+ </a>
35
47
  </div>
36
48
  {% endif %}
49
+
37
50
  {% if media.type == "video" %}
38
- {% set mhash = media.url|replace('.', '')|replace(' ', '') %}
39
- <div class="{{ column_size_t }} columns thumbnail">
40
- {% if 'sigal.plugins.media_page' in settings.plugins %}
41
- <a href="{{ media.url }}.html" data-href="#{{ mhash }}"
42
- {% else %}
43
- <a href="#{{ mhash }}" class="gallery" inline='yes' title="{{ media.url }}"
44
- {% endif %}
45
- {% if media.big %} data-big="{{ media.big_url }}"{% endif %}>
46
- <img src="{{ media.thumbnail }}" alt="{{ media.url }}"
47
- title="{{ media.title }}" /></a>
48
- </div>
49
- <!-- This contains the hidden content for the video -->
50
- <div style='display:none'>
51
- <div id="{{ mhash }}">
52
- <video controls>
53
- <source src='{{ media.url }}' type='{{ media.mime }}' />
54
- </video>
55
- </div>
51
+ <!-- This contains the hidden content for the video -->
52
+ <div style='display:none'>
53
+ <div id="{{ mhash }}">
54
+ <video controls>
55
+ <source src='{{ media.url }}' type='{{ media.mime }}' />
56
+ </video>
56
57
  </div>
58
+ </div>
57
59
  {% endif %}
58
60
  {% if loop.last or loop.index % nb_columns == 0 %}
59
- </div>
61
+ </div>
60
62
  {% endif%}
61
63
  {% endfor %}
62
64
  </div>
@@ -27,7 +27,7 @@
27
27
  {% for media in album.medias -%}
28
28
  {
29
29
  title: "{{ media.title }}",
30
- description: "{{ img_description(media) | e }}",
30
+ description: {{ img_description(media) | tojson }},
31
31
  thumb: "{{ media.thumbnail }}",
32
32
  {% if media.big %}
33
33
  big: "{{ media.big_url }}",
@@ -175,12 +175,8 @@ figcaption {
175
175
  line-height: 0;
176
176
  }
177
177
 
178
- .gallery__img--main a{
179
- width: auto;
180
- height: auto;
181
- }
182
-
183
178
  .menu-img,
179
+ .gallery__img--main,
184
180
  .gallery__img--secondary {
185
181
  width: 25%;
186
182
  float:left;
@@ -213,6 +209,7 @@ footer span:not(:last-child):after {
213
209
  font-size: 40px;
214
210
  }
215
211
  .menu-img,
212
+ .gallery__img--main,
216
213
  .gallery__img--secondary {
217
214
  width: 33%;
218
215
  }
@@ -221,6 +218,7 @@ footer span:not(:last-child):after {
221
218
 
222
219
  @media screen and (max-width: 500px) {
223
220
  .menu-img,
221
+ .gallery__img--main,
224
222
  .gallery__img--secondary {
225
223
  width: 50%;
226
224
  }
@@ -229,6 +227,7 @@ footer span:not(:last-child):after {
229
227
 
230
228
  @media screen and (max-width: 300px) {
231
229
  .menu-img,
230
+ .gallery__img--main,
232
231
  .gallery__img--secondary {
233
232
  width: 100%;
234
233
  }
@@ -1,92 +1,8 @@
1
1
  {% from 'description.html' import img_description %}
2
2
  {% extends "base.html" %}
3
3
 
4
- {% block extra_head %}
5
- <link rel="stylesheet" href="{{ theme.url }}/photoswipe.css">
6
- <script type="module">
7
- import PhotoSwipeLightbox from '/static/photoswipe-lightbox.esm.min.js';
8
- import PhotoSwipe from '/static/photoswipe.esm.min.js';
9
- import PhotoSwipeDynamicCaption from '/static/photoswipe-dynamic-caption-plugin.esm.min.js';
10
- import PhotoSwipeFullscreen from '/static/photoswipe-fullscreen.esm.min.js';
11
- import PhotoSwipeVideoPlugin from '/static/photoswipe-video-plugin.esm.min.js';
12
-
13
- const lightbox = new PhotoSwipeLightbox({
14
- gallery: '.gallery',
15
- children: '.thumbnail',
16
- pswpModule: PhotoSwipe
17
- });
18
- const captionPlugin = new PhotoSwipeDynamicCaption(lightbox, {type: 'auto'});
19
- const fullscreenPlugin = new PhotoSwipeFullscreen(lightbox);
20
- const videoPlugin = new PhotoSwipeVideoPlugin(lightbox, {autoplay: true});
21
-
22
- lightbox.on('uiRegister', function() {
23
- lightbox.pswp.ui.registerElement({
24
- name: 'download-button',
25
- order: 8,
26
- isButton: true,
27
- tagName: 'a',
28
- html: {
29
- isCustomSVG: true,
30
- inner: '<path d="M20.5 14.3 17.1 18V10h-2.2v7.9l-3.4-3.6L10 16l6 6.1 6-6.1ZM23 23H9v2h14Z" id="pswp__icn-download"/>',
31
- outlineID: 'pswp__icn-download'
32
- },
33
- onInit: (el, pswp) => {
34
- el.setAttribute('download', '');
35
- el.setAttribute('target', '_blank');
36
- el.setAttribute('rel', 'noopener');
37
- pswp.on('change', () => {
38
- if (pswp.currSlide.data.element.children[0].dataset.big) {
39
- el.href = pswp.currSlide.data.element.children[0].dataset.big;
40
- } else {
41
- el.href = pswp.currSlide.data.src;
42
- }
43
- });
44
- }
45
- });
46
- });
47
-
48
- lightbox.init();
49
- </script>
50
- {% endblock extra_head %}
51
-
52
4
  {% block content %}
53
- {% include 'download_zip.html' %}
54
- {% include 'map.html' %}
55
- <div class="gallery">
56
- {% for media in album.medias %}
57
- {% if media.type == "image" %}
58
- <figure class="gallery__img--{{ "main" if loop.first else "secondary" }} thumbnail">
59
- <a href="{{ media.url }}"
60
- data-pswp-width="{{media.size.width}}"
61
- data-pswp-height="{{media.size.height}}"
62
- {%- if media.big -%}
63
- data-big="{{ media.big_url }}"
64
- {%- endif -%}
65
- >
66
- <img src="{{ media.thumbnail }}" alt="{{ media.url }}" />
67
- </a>
68
- <div class="pswp-caption-content">
69
- {{ img_description(media, with_big=False) }}
70
- </div>
71
- <figcaption>{{ media.title }} - {{ media.exif.datetime }}</figcaption>
72
- </figure>
73
- {% endif %}
74
- {% if media.type == "video" %}
75
- <figure class="gallery__img--secondary thumbnail" >
76
- <a href="{{ media.url }}"
77
- data-pswp-type="video"
78
- data-pswp-width="800"
79
- data-pswp-height="600">
80
- <img src="{{ media.thumbnail }}" alt="{{ media.url }}" />
81
- </a>
82
- <div class="pswp-caption-content">
83
- {{ img_description(media, with_big=False) }}
84
- </div>
85
- <figcaption>{{ media_title }}</figcaption>
86
- </figure>
87
- {% endif %}
88
- {% endfor %}
89
- </div>
5
+ {% include "album_items.html" %}
90
6
  {% endblock %}
91
7
 
92
8
  {% block extra_footer %}
@@ -0,0 +1,31 @@
1
+ {% from 'description.html' import img_description %}
2
+
3
+ {% include 'download_zip.html' %}
4
+ {% include 'map.html' %}
5
+ <div class="gallery">
6
+ {% for media in album.medias %}
7
+ {% if media.thumbnail %}
8
+ <figure class="gallery__img--{{ "main" if loop.first else "secondary" }} thumbnail">
9
+ <a href="{{ media.url }}"
10
+ {% if media.type == "image" %}
11
+ data-pswp-width="{{media.size.width}}"
12
+ data-pswp-height="{{media.size.height}}"
13
+ {% if media.big %}
14
+ data-big="{{ media.big_url }}"
15
+ {%- endif -%}
16
+ {% elif media.type == "video" %}
17
+ data-pswp-type="video"
18
+ data-pswp-width="800"
19
+ data-pswp-height="600"
20
+ {% endif %}
21
+ >
22
+ <img src="{{ media.thumbnail }}" alt="{{ media.url }}" />
23
+ </a>
24
+ <div class="pswp-caption-content">
25
+ {{ img_description(media, with_big=False) }}
26
+ </div>
27
+ <figcaption>{{ media.title }}{{ (" - " + media.exif.datetime) if media.exif else "" }}</figcaption>
28
+ </figure>
29
+ {% endif %}
30
+ {% endfor %}
31
+ </div>
@@ -1,4 +1,5 @@
1
1
  {% extends "base.html" %}
2
+
2
3
  {% block content %}
3
4
  <div class="album-list">
4
5
  {% for alb in album.albums %}
@@ -10,4 +11,8 @@
10
11
  </div>
11
12
  {% endfor %}
12
13
  </div>
14
+
15
+ {% if album.medias %}
16
+ {% include "album_items.html" %}
17
+ {% endif %}
13
18
  {% endblock %}
@@ -9,6 +9,55 @@
9
9
  <meta name="author" content="{{ album.author }}">
10
10
  <meta name="viewport" content="width=device-width">
11
11
  {% include 'default_head.html' %}
12
+
13
+ {% if album.medias %}
14
+ <link rel="stylesheet" href="{{ theme.url }}/photoswipe.css">
15
+ <script type="module">
16
+ import PhotoSwipeLightbox from '{{ theme.url }}/photoswipe-lightbox.esm.min.js';
17
+ import PhotoSwipe from '{{ theme.url }}/photoswipe.esm.min.js';
18
+ import PhotoSwipeDynamicCaption from '{{ theme.url }}/photoswipe-dynamic-caption-plugin.esm.min.js';
19
+ import PhotoSwipeFullscreen from '{{ theme.url }}/photoswipe-fullscreen.esm.min.js';
20
+ import PhotoSwipeVideoPlugin from '{{ theme.url }}/photoswipe-video-plugin.esm.min.js';
21
+
22
+ const lightbox = new PhotoSwipeLightbox({
23
+ gallery: '.gallery',
24
+ children: '.thumbnail',
25
+ pswpModule: PhotoSwipe
26
+ });
27
+ const captionPlugin = new PhotoSwipeDynamicCaption(lightbox, {type: 'auto'});
28
+ const fullscreenPlugin = new PhotoSwipeFullscreen(lightbox);
29
+ const videoPlugin = new PhotoSwipeVideoPlugin(lightbox, {autoplay: true});
30
+
31
+ lightbox.on('uiRegister', function() {
32
+ lightbox.pswp.ui.registerElement({
33
+ name: 'download-button',
34
+ order: 8,
35
+ isButton: true,
36
+ tagName: 'a',
37
+ html: {
38
+ isCustomSVG: true,
39
+ inner: '<path d="M20.5 14.3 17.1 18V10h-2.2v7.9l-3.4-3.6L10 16l6 6.1 6-6.1ZM23 23H9v2h14Z" id="pswp__icn-download"/>',
40
+ outlineID: 'pswp__icn-download'
41
+ },
42
+ onInit: (el, pswp) => {
43
+ el.setAttribute('download', '');
44
+ el.setAttribute('target', '_blank');
45
+ el.setAttribute('rel', 'noopener');
46
+ pswp.on('change', () => {
47
+ if (pswp.currSlide.data.element.children[0].dataset.big) {
48
+ el.href = pswp.currSlide.data.element.children[0].dataset.big;
49
+ } else {
50
+ el.href = pswp.currSlide.data.src;
51
+ }
52
+ });
53
+ }
54
+ });
55
+ });
56
+
57
+ lightbox.init();
58
+ </script>
59
+ {% endif %}
60
+
12
61
  {% block extra_head %}{% endblock extra_head %}
13
62
  <link rel="stylesheet" href="{{ theme.url }}/styles.css">
14
63
  {% if user_css %}
sigal/utils.py CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2011-2023 - Simon Conseil
1
+ # Copyright (c) 2011-2026 - Simon Conseil
2
2
 
3
3
  # Permission is hereby granted, free of charge, to any person obtaining a copy
4
4
  # of this software and associated documentation files (the "Software"), to
sigal/version.py CHANGED
@@ -1,7 +1,14 @@
1
1
  # file generated by setuptools-scm
2
2
  # don't change, don't track in version control
3
3
 
4
- __all__ = ["__version__", "__version_tuple__", "version", "version_tuple"]
4
+ __all__ = [
5
+ "__version__",
6
+ "__version_tuple__",
7
+ "version",
8
+ "version_tuple",
9
+ "__commit_id__",
10
+ "commit_id",
11
+ ]
5
12
 
6
13
  TYPE_CHECKING = False
7
14
  if TYPE_CHECKING:
@@ -9,13 +16,19 @@ if TYPE_CHECKING:
9
16
  from typing import Union
10
17
 
11
18
  VERSION_TUPLE = Tuple[Union[int, str], ...]
19
+ COMMIT_ID = Union[str, None]
12
20
  else:
13
21
  VERSION_TUPLE = object
22
+ COMMIT_ID = object
14
23
 
15
24
  version: str
16
25
  __version__: str
17
26
  __version_tuple__: VERSION_TUPLE
18
27
  version_tuple: VERSION_TUPLE
28
+ commit_id: COMMIT_ID
29
+ __commit_id__: COMMIT_ID
19
30
 
20
- __version__ = version = '2.5'
21
- __version_tuple__ = version_tuple = (2, 5)
31
+ __version__ = version = '2.6'
32
+ __version_tuple__ = version_tuple = (2, 6)
33
+
34
+ __commit_id__ = commit_id = 'gdd348d1f4'
sigal/video.py CHANGED
@@ -1,5 +1,5 @@
1
1
  # Copyright (c) 2013 - Christophe-Marie Duquesne
2
- # Copyright (c) 2013-2023 - Simon Conseil
2
+ # Copyright (c) 2013-2026 - Simon Conseil
3
3
  # Copyright (c) 2021 - Keith Feldman
4
4
 
5
5
  # Permission is hereby granted, free of charge, to any person obtaining a copy
sigal/writer.py CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2009-2023 - Simon Conseil
1
+ # Copyright (c) 2009-2026 - Simon Conseil
2
2
  # Copyright (c) 2013 - Christophe-Marie Duquesne
3
3
  # Copyright (c) 2018 - Edwin Steele
4
4
 
@@ -1,28 +1,27 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: sigal
3
- Version: 2.5
3
+ Version: 2.6
4
4
  Summary: Simple static gallery generator
5
5
  Author-email: Simon Conseil <contact@saimon.org>
6
- License: MIT License
6
+ License-Expression: MIT
7
7
  Project-URL: repository, https://github.com/saimn/sigal
8
- Project-URL: documentation, http://sigal.saimon.org/en/latest/
8
+ Project-URL: documentation, https://sigal.readthedocs.io/en/latest/
9
9
  Keywords: gallery,static,generator,image,video,galleria
10
10
  Classifier: Development Status :: 5 - Production/Stable
11
11
  Classifier: Environment :: Console
12
- Classifier: License :: OSI Approved :: MIT License
13
12
  Classifier: Operating System :: OS Independent
14
13
  Classifier: Programming Language :: Python :: 3
15
14
  Classifier: Topic :: Internet :: WWW/HTTP
16
15
  Classifier: Topic :: Multimedia :: Graphics :: Viewers
17
16
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
18
- Requires-Python: >=3.9
17
+ Requires-Python: >=3.11
19
18
  Description-Content-Type: text/x-rst
20
19
  License-File: LICENSE
21
20
  Requires-Dist: blinker
22
21
  Requires-Dist: click
23
22
  Requires-Dist: Jinja2>=2.7
24
23
  Requires-Dist: Markdown
25
- Requires-Dist: Pillow>=8.0.0
24
+ Requires-Dist: Pillow>=10.0.0
26
25
  Requires-Dist: pilkit
27
26
  Requires-Dist: natsort
28
27
  Provides-Extra: all
@@ -31,6 +30,7 @@ Requires-Dist: feedgenerator; extra == "all"
31
30
  Requires-Dist: zopfli; extra == "all"
32
31
  Requires-Dist: cryptography; extra == "all"
33
32
  Requires-Dist: pillow-heif; extra == "all"
33
+ Requires-Dist: pdf2image; extra == "all"
34
34
  Provides-Extra: tests
35
35
  Requires-Dist: pytest; extra == "tests"
36
36
  Requires-Dist: pytest-cov; extra == "tests"
@@ -38,6 +38,7 @@ Provides-Extra: docs
38
38
  Requires-Dist: Sphinx>=4.1.0; extra == "docs"
39
39
  Requires-Dist: furo; extra == "docs"
40
40
  Requires-Dist: cryptography; extra == "docs"
41
+ Dynamic: license-file
41
42
 
42
43
  Sigal - Simple Static Gallery Generator
43
44
  =======================================
@@ -64,7 +65,7 @@ The idea behind Sigal is to ease the use of the javascript libraries like
64
65
  galleria_. These libraries do a great job to display the images, Sigal does
65
66
  what is missing: resize images, create thumbnails, generate HTML pages.
66
67
 
67
- Sigal requires Python 3.9+.
68
+ Sigal requires Python 3.11+.
68
69
 
69
70
  Links
70
71
  -----
@@ -83,7 +84,7 @@ Javascript libraries:
83
84
  - `galleria demo`_
84
85
  - `photoswipe demo`_
85
86
 
86
- .. _website: http://sigal.saimon.org/
87
+ .. _website: https://sigal.readthedocs.io/en/latest/
87
88
  .. _GitHub: https://github.com/saimn/sigal/
88
89
  .. _PyPI: https://pypi.org/project/sigal/
89
90
  .. _galleria: https://github.com/GalleriaJS/galleria
@@ -1,23 +1,23 @@
1
- sigal/__init__.py,sha256=XmFtGi1i9IHJwfO6PSo2zcHVME2fCUHi8z7goxyF66Q,1258
2
- sigal/__main__.py,sha256=YUJZmHYjp3r4yJFWA2AaAbEemskQpibqhs_6jtRHQ84,9587
3
- sigal/gallery.py,sha256=JjQE_4GkNMy9IGNDRxKI6j28_7pPkuQvse_qRJIDDF0,34716
4
- sigal/image.py,sha256=EnS8-ADcub3lKIex7ymWEjkjXPtZidqcCDDqWOfNq94,13425
5
- sigal/log.py,sha256=aDCoUdXZmnyNntkGJng2rmTpWlOZgbCVhkDc0ZqpqPA,2588
6
- sigal/settings.py,sha256=zojDBZnPF6ArYuzrl1u9cXYcKgBiat-ZZLKl_m2vgkQ,6272
1
+ sigal/__init__.py,sha256=kfl9IcTdz5YgXNo8WtzE7jl9ET_KoCFTRsKLkOb3OSo,1258
2
+ sigal/__main__.py,sha256=A8M5r85oxDMhvv5hRH4f1A0F7VxfGYqnYYaap3gVgQ4,9640
3
+ sigal/gallery.py,sha256=1b4xef21hp9_Q3faXVYaal-84ADfx_paESu2sDTwbts,33981
4
+ sigal/image.py,sha256=2iEAhZcmbPG_AM7UXrO3hxTJPXbQo1Fme_eHV34nULE,13612
5
+ sigal/log.py,sha256=WJGnjLaB2pLdBI3QFwtkYKCHYxxzvWZc6K6O3lpfwWI,2588
6
+ sigal/settings.py,sha256=uSoPnl3dcLSV9LM7KonOmBcJ4WflrKUHYj4tu5u4-js,6793
7
7
  sigal/signals.py,sha256=Q2qcr3DKnGxs_n-6SDKSA7aEVcX2ss-gZLgU6CO7uzY,445
8
- sigal/utils.py,sha256=Tk0g0phzaW3KlbeOL0u-T6y46oBxMp7kPt5pbTLtGAo,5811
9
- sigal/version.py,sha256=El14jpQdZIYub_dNYjZnU9XK_ijjLHH4ar3Gmj4wMAA,506
10
- sigal/video.py,sha256=CwD7CyoD4Fv1eUDHVOW55TaDt_7gLqU7TLg0klup9tE,9721
11
- sigal/writer.py,sha256=RFN1GxZpSsUrQvZyy7eSVGID7UD8NGsdhSEWoTiZIRE,6562
8
+ sigal/utils.py,sha256=zL_RRdi-JIK0C3jE1uY8oI6GKJjYYYO3qtnx1mP0ylU,5811
9
+ sigal/version.py,sha256=3kKbOWd2qVzKIail3EFjexJBsFuOfXbfVu5-VQxIRrM,707
10
+ sigal/video.py,sha256=6jfPziFotWEOpqgiulD81QCP3OssDtXojgvFnjSLCfI,9721
11
+ sigal/writer.py,sha256=rATxxlB_89TROARgv3E684ZPm0NiJb5RJdEN_roRGfk,6562
12
12
  sigal/plugins/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
13
  sigal/plugins/adjust.py,sha256=CeWNWL4X2JkONSAVxKAYHuhfWwGICvhtVYqWIGNX2M0,761
14
14
  sigal/plugins/compress_assets.py,sha256=PYMUa3w0euoBIfNlVaryzSlmRfpmNCBn58DO4LX163o,5443
15
15
  sigal/plugins/copyright.py,sha256=TKzdFYBbBoAqGsPuVjjprXF6qIChUu6Tdi5P6v9dP28,2018
16
16
  sigal/plugins/extended_caching.py,sha256=h3Z9h24wUMRcEoJ3-xTiiCwyodmjdHv-t8bLf9DialQ,6232
17
- sigal/plugins/feeds.py,sha256=dl-c-OLS8DPGLVP-agaQ3WxFdmw4iLchfs8DsAd0rPI,2821
18
- sigal/plugins/media_page.py,sha256=oe1lAlnze-VxVxlXHc3urcLNyIq5jvveElsh5JC5-Ls,3201
17
+ sigal/plugins/feeds.py,sha256=XqyXZlVKZeQY4XpmUjjcauZ-4pskZ032BQUbZy8kinE,2847
18
+ sigal/plugins/media_page.py,sha256=fR69jqGH-gR9ZXBH0JXKunrE-TxOvQ-TTD3w9j-EPJI,3201
19
19
  sigal/plugins/nomedia.py,sha256=ggZdkvRfFHwgv5u2-okWXdSHVq2oqQCRdtVIhj88wFM,4961
20
- sigal/plugins/nonmedia_files.py,sha256=S7VCEvgSrKBnCJpJOf6Vzakv04UCZjkrJUyeo3EoRiI,5024
20
+ sigal/plugins/nonmedia_files.py,sha256=im4B_-GA23AQDmTh0kqQcMcac8-3grOOx1VsdFF_AQk,5488
21
21
  sigal/plugins/titleregexp.py,sha256=JbiPnlOQ6xB8M5cABuoCCMmWtxe6AvKh9nmcvZmEoH0,3737
22
22
  sigal/plugins/watermark.py,sha256=EstbcrAwCtV9Iy1V9odUFK6qmUKL4wNVo_1QM5ONwdA,3739
23
23
  sigal/plugins/zip_gallery.py,sha256=2BOAqXiMoWNrgW4LvGRwyIpH-GeGCUFXvGiruPDMR9Y,4651
@@ -41,7 +41,7 @@ sigal/themes/colorbox/static/js/jquery.colorbox-min.js,sha256=QbxNT-iBOdbuiav8sq
41
41
  sigal/themes/colorbox/static/js/jquery.colorbox.js,sha256=WYuzmp4s4GsP0fs-5V6iHJVa-ZbXywhZgnHyaJ95vSU,29168
42
42
  sigal/themes/colorbox/static/js/jquery.touchSwipe.js,sha256=LV72kccF1eiTVmLaVJ4__wZkLluKeBUrdyOiy4PvBMs,78338
43
43
  sigal/themes/colorbox/static/js/jquery.touchSwipe.min.js,sha256=kWfLN-0hQF75VWZGuDeJogmcFTmPXOqGdHDfHoF1Lhc,20430
44
- sigal/themes/colorbox/templates/album.html,sha256=nwUVMxhSN2Iq0K63TBjgvxqKqj-XNz9iMpCY-xsI7QE,2943
44
+ sigal/themes/colorbox/templates/album.html,sha256=VA_WZom21gY2cvMrhI5zyczOmF1X1I6nnY7MOsxNFow,2763
45
45
  sigal/themes/colorbox/templates/album_list.html,sha256=giJFHT8vqMbNjT8wN2H9v9T2urMgjJq1RMJjB7tZcrw,817
46
46
  sigal/themes/colorbox/templates/base.html,sha256=x2W0zxu3-8F40PcYmZQICaoUAFmsBTvENQ_B7WH-7Vw,1874
47
47
  sigal/themes/colorbox/templates/media.html,sha256=PMb4TgjwQnHgrfiR37kYvN-S4gvlMVEs9lekafZYXI4,888
@@ -113,7 +113,7 @@ sigal/themes/galleria/static/themes/twelve/galleria.twelve.js,sha256=cEH3RivOPny
113
113
  sigal/themes/galleria/static/themes/twelve/galleria.twelve.min.css,sha256=BJ0sIDeTVuHynQmREmKcZoRDNLaxgh1zElt6_14E0VI,48127
114
114
  sigal/themes/galleria/static/themes/twelve/galleria.twelve.min.js,sha256=h5FDcg3UFnAMAMrel0li0XcPPYnPfoWZwX5P_Wzhpz0,4256
115
115
  sigal/themes/galleria/templates/album.html,sha256=qqEcZL-aaRjBznixrGSZcIC3Ene41LsaSf8i7QXdrD0,478
116
- sigal/themes/galleria/templates/album_items.html,sha256=Mx-HqEg9e_mfE06HbNpBEh5FFjFhwmfyoVzxKuEH70w,2580
116
+ sigal/themes/galleria/templates/album_items.html,sha256=vb1RIEjnhXkvX03_f0mvT1A2WqhA4l0ieF0Z7R9GpQ8,2583
117
117
  sigal/themes/galleria/templates/album_list.html,sha256=I3GkHil8wNa0K70kbS0Qnns5iGlvaZqwwtYLNKPZIwo,749
118
118
  sigal/themes/galleria/templates/base.html,sha256=FE7kBmWSR0wCQJBnHUYJ3ONGLA1Auo00jMYXMUVTxvE,1462
119
119
  sigal/themes/photoswipe/static/photoswipe-dynamic-caption-plugin.esm.js,sha256=F6istdJw3AAK8N1lZ8IkfMO3m7DhjDGomHzdof2Vlrk,12651
@@ -129,13 +129,14 @@ sigal/themes/photoswipe/static/photoswipe.css,sha256=u16VaBKoE-G9W-XXZBzHjQXX59P
129
129
  sigal/themes/photoswipe/static/photoswipe.esm.js,sha256=23YN69O4wN_-5QPeKjYeRfCp9-s8dqlz47vaVBErhBc,187718
130
130
  sigal/themes/photoswipe/static/photoswipe.esm.js.map,sha256=kEmuw6JFgJmMY0ACMSSu1NuN0hO4_ZqKeTxjqDWVo2Q,379188
131
131
  sigal/themes/photoswipe/static/photoswipe.esm.min.js,sha256=VCBpdxvrNNxGHNuTdNqK9kPFkev2XY7DYzHdmgaB69Q,54270
132
- sigal/themes/photoswipe/static/styles.css,sha256=pr8tKKTPzUfquQNXXn4FvBlZFVemiY4gRU86Y1FeIpM,4211
133
- sigal/themes/photoswipe/templates/album.html,sha256=3Q5aDVjHgDBZr7uIAg2iDvsUukAe-AbFxlegJcBG3ZU,3301
134
- sigal/themes/photoswipe/templates/album_list.html,sha256=lZh2TLFqKOSfWaqAaZyutCxPzxTcogRbibyc6-2YsgU,403
135
- sigal/themes/photoswipe/templates/base.html,sha256=S-lhAJfUkXJ9E6Q0FAzrbnv1kM1rW0PpAxKARDR1s8c,1284
136
- sigal-2.5.dist-info/LICENSE,sha256=VFPg0QyH2MjVAWTOJrYce-Q6DJydrasigpGut33O7cY,1086
137
- sigal-2.5.dist-info/METADATA,sha256=NpHfYREuF1l3eU1ANeuGMQ0sDn0OGaAVG_c8QWxWuos,3273
138
- sigal-2.5.dist-info/WHEEL,sha256=nn6H5-ilmfVryoAQl3ZQ2l8SH5imPWFpm1A5FgEuFV4,91
139
- sigal-2.5.dist-info/entry_points.txt,sha256=9MBYGKDTQgjtKk7ebYjjdtXGI50AAaJ8q1TG9HsE2QM,46
140
- sigal-2.5.dist-info/top_level.txt,sha256=z6e1_OpCyg5rzqDmiAat3ytylvaz0K5F7VDdrbF8fQc,6
141
- sigal-2.5.dist-info/RECORD,,
132
+ sigal/themes/photoswipe/static/styles.css,sha256=wI0teLiciN0PEY3F1B1CrRPh5kxjx9QI0k5Bfxz8uu8,4244
133
+ sigal/themes/photoswipe/templates/album.html,sha256=RSEDvpC_a70a6kGdSBzm3RzekhVd4W_cZdQFQ8ssia8,191
134
+ sigal/themes/photoswipe/templates/album_items.html,sha256=9N3pq6-7j0lS7QZ80XMY3Vq4QIuXZhA44yiKuSYf5cs,1147
135
+ sigal/themes/photoswipe/templates/album_list.html,sha256=DKpY331AETSIGhmTpzLla__l1OGEnkrVitg0Y4RaQOw,480
136
+ sigal/themes/photoswipe/templates/base.html,sha256=Q7IPBXAANmClRpcDUFSQ-dSnLVNVMVG9uRr-JCwLsmk,3142
137
+ sigal-2.6.dist-info/licenses/LICENSE,sha256=eFdFqW_W6LfS1bMybEIUNqQ5pUybPVE9WT-Zdk_Acow,1086
138
+ sigal-2.6.dist-info/METADATA,sha256=aR_XRshXssSDkd9juMi04P9j93joUR-kVitbBCJ9Nmo,3311
139
+ sigal-2.6.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
140
+ sigal-2.6.dist-info/entry_points.txt,sha256=9MBYGKDTQgjtKk7ebYjjdtXGI50AAaJ8q1TG9HsE2QM,46
141
+ sigal-2.6.dist-info/top_level.txt,sha256=z6e1_OpCyg5rzqDmiAat3ytylvaz0K5F7VDdrbF8fQc,6
142
+ sigal-2.6.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.8.1)
2
+ Generator: setuptools (80.10.2)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,5 +1,5 @@
1
1
  The MIT License (MIT)
2
- Copyright (c) 2009-2023 - Simon Conseil
2
+ Copyright (c) 2009-2026 - Simon Conseil
3
3
 
4
4
  Permission is hereby granted, free of charge, to any person obtaining a copy
5
5
  of this software and associated documentation files (the "Software"), to