jekyll-image-gallery 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.rubocop.yml +12 -0
- data/CHANGELOG.md +3 -0
- data/Gemfile +8 -0
- data/LICENSE.txt +21 -0
- data/README.md +119 -0
- data/Rakefile +4 -0
- data/jekyll-image-gallery.gemspec +36 -0
- data/lib/gallery/gallery.rb +386 -0
- data/lib/gallery/version.rb +5 -0
- data/lib/jekyll-image-gallery.rb +6 -0
- metadata +112 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 849fa5460b1ac2e8044abf0d13eae3b2982038c0c753f511e0ec17179fcdcbf0
|
4
|
+
data.tar.gz: 99b257f6d9aa547a0a117f20bb188fcdc29a0c2ee2635e170844e679d2add3b2
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 132565ef429e45232019f138c4a45d383c9b27be8049e5f76f6dd48082550528627e2a98836799ba1888147d3ba1c3e22e8fae8817b1b037ce9324363d0da88c
|
7
|
+
data.tar.gz: f1ec6ddc49b2ed9336e12470e85a9856a07be917482fa6bc55681586b93301ac8b46638c27475f4f5f58d8c8a27bc0f93fa7b1c215333d3390070a8b82473d3d
|
data/.rubocop.yml
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
Layout/LineLength:
|
2
|
+
Max: 120
|
3
|
+
|
4
|
+
Style/TrailingCommaInArrayLiteral:
|
5
|
+
Description: Prefer always trailing comma on multiline literals. This makes diffs, and re-ordering nicer.
|
6
|
+
EnforcedStyleForMultiline: consistent_comma
|
7
|
+
|
8
|
+
Style/TrailingCommaInHashLiteral:
|
9
|
+
Description: Prefer always trailing comma on multiline literals. This makes diffs, and re-ordering nicer.
|
10
|
+
EnforcedStyleForMultiline: consistent_comma
|
11
|
+
|
12
|
+
|
data/CHANGELOG.md
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2022 TODO: Write your name
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,119 @@
|
|
1
|
+
# Jekyll Image Gallery
|
2
|
+
A image gallery generator for Jekyll. The differentiating feature from other gallery plugins is that it generates galleries
|
3
|
+
automatically grouped into years.
|
4
|
+
|
5
|
+
The feature list is pretty standard:
|
6
|
+
- Thumbnail generation
|
7
|
+
- Optional image compression
|
8
|
+
- Exif data stripping
|
9
|
+
- Automatic conversion of raw formats (e.g. [HEIC](https://en.wikipedia.org/wiki/High_Efficiency_Image_File_Format)) into browser supported formats
|
10
|
+
|
11
|
+
Example can be found at [photos.jacobessex.com](https://photos.jacobessex.com) \[[source](https://github.com/Yacoby/photos.jacobessex.com)\]
|
12
|
+
|
13
|
+
### Alternatives
|
14
|
+
|
15
|
+
Also consider:
|
16
|
+
- [cheesy-gallery](https://github.com/DavidS/cheesy-gallery) - Allows more flexibility in structure
|
17
|
+
- [jekyll-gallery-generator](https://github.com/ggreer/jekyll-gallery-generator) - Similar to this plugin but without the year based grouping
|
18
|
+
|
19
|
+
## Installation
|
20
|
+
See the [Jekyll guide on installing plugins](https://jekyllrb.com/docs/plugins/installation/) and install "jekyll-image-gallery"
|
21
|
+
|
22
|
+
Ensure you install the transitive dependency ImageMagick, as this is required by the [mini_magick](https://github.com/minimagick/minimagick) gem. On Mac this is as simple as
|
23
|
+
```
|
24
|
+
$ brew install imagemagick
|
25
|
+
```
|
26
|
+
|
27
|
+
## Usage
|
28
|
+
|
29
|
+
After installation, create a `_galleries` directory and within the `_galleries` directory, create sub-directories for each gallery. For example your directory structure could look like this:
|
30
|
+
|
31
|
+
```
|
32
|
+
- _galleries/
|
33
|
+
--- 2022/
|
34
|
+
----- 2022-05-23-first-gallery/
|
35
|
+
------- first_image.jpg
|
36
|
+
------- second_image.png
|
37
|
+
----- 2022-09-01-second/
|
38
|
+
------- awesome.jpg
|
39
|
+
```
|
40
|
+
|
41
|
+
This will result in the generated galleries:
|
42
|
+
```
|
43
|
+
- _site/
|
44
|
+
--- index.html (optional root index.html)
|
45
|
+
--- 2022/
|
46
|
+
----- index.html
|
47
|
+
----- 05/
|
48
|
+
------- first-gallery.html
|
49
|
+
------- first_image.jpg
|
50
|
+
------- second_image.png
|
51
|
+
------- [.. and thumbnails ..]
|
52
|
+
----- 09/
|
53
|
+
------- second.html
|
54
|
+
------- awesome.jpg
|
55
|
+
------- [.. and thumbnails ..]
|
56
|
+
```
|
57
|
+
|
58
|
+
As you can see from the example, the directory structure you use doesn't matter, each sub directory containing images will be considered its own gallery (even subdirectories nested in an existing gallery) and the structure will be based on the year and the month of each gallery.
|
59
|
+
|
60
|
+
The year and month of the gallery is inferred from the first of
|
61
|
+
- The \_metadata file (see the "Per gallery configuration" section), if it exists and the key is set
|
62
|
+
- The earliest date contained in image Exif data if the images have Exif data
|
63
|
+
- The gallery directory name (if suffixed with an date)
|
64
|
+
|
65
|
+
### Layouts
|
66
|
+
|
67
|
+
This plugin generates pages which use two layouts
|
68
|
+
- `gallery_index` - an index of galleries
|
69
|
+
- `gallery_page` - the gallery itself
|
70
|
+
|
71
|
+
These layouts are not included in the plugin and so you will need to implement them yourself. An example can be found [here](https://github.com/Yacoby/photos.jacobessex.com/tree/master/_layouts)
|
72
|
+
|
73
|
+
### Highlighted image
|
74
|
+
|
75
|
+
The gallery index page displays a thumbnail for the image gallery. By default this is the first image but a specific image
|
76
|
+
can be chosen by prefixing the image name with `hl_` or suffixing it with `_hl`. e.g. `myimage_hl.png`.
|
77
|
+
|
78
|
+
### Plugin Configuration
|
79
|
+
|
80
|
+
Global gallery configuration uses the standard Jekyll configuration (default of `_config.yaml`)
|
81
|
+
|
82
|
+
```yaml
|
83
|
+
gallery:
|
84
|
+
generate_root_index: true # Generate a index gallery at the root of the gallery (default false)
|
85
|
+
path: 'some/path' # Slash seperated output path of the gallery, set to an empty string to generate a gallery in the root of the site
|
86
|
+
# (default is 'gallery')
|
87
|
+
title_prefix: 'Photos' # The prefix of the title variable (default is 'Photos')
|
88
|
+
title_seperator: '|' # The seperator between the title components (prefix, year and gallery title). (default is '|')
|
89
|
+
|
90
|
+
thumbnail_size: # Size of generated thumbnails
|
91
|
+
x: 512
|
92
|
+
y: 512
|
93
|
+
|
94
|
+
image_size: # The maximum size of images, remove the configuration to not compress images (default is no compression)
|
95
|
+
x: 2048
|
96
|
+
y: 2048
|
97
|
+
```
|
98
|
+
|
99
|
+
### Per gallery configuration
|
100
|
+
|
101
|
+
Gallery configuration can be overridden by adding a `_metadata.toml` or `_metadata.json` file to the gallery directory.
|
102
|
+
|
103
|
+
```toml
|
104
|
+
name = "Gallery Name"
|
105
|
+
datetime = "2001-02-03T04:05:06+07:00"
|
106
|
+
```
|
107
|
+
|
108
|
+
The datetime field is parsed using the [Datetime::parse method](https://ruby-doc.org/stdlib-2.6.1/libdoc/date/rdoc/DateTime.html)
|
109
|
+
|
110
|
+
|
111
|
+
## Development
|
112
|
+
|
113
|
+
After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
114
|
+
|
115
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
116
|
+
|
117
|
+
## License
|
118
|
+
|
119
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'lib/gallery/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = 'jekyll-image-gallery'
|
7
|
+
spec.version = ImageGallery::VERSION
|
8
|
+
spec.authors = ['Jacob Essex']
|
9
|
+
spec.email = ['ruby_gems@JacobEssex.com']
|
10
|
+
|
11
|
+
spec.summary = 'Gallery generator for Jekyll'
|
12
|
+
spec.homepage = 'https://github.com/Yacoby/jekyll-image-gallery'
|
13
|
+
spec.license = 'MIT'
|
14
|
+
spec.required_ruby_version = '>= 2.6.0'
|
15
|
+
|
16
|
+
spec.metadata['homepage_uri'] = spec.homepage
|
17
|
+
spec.metadata['source_code_uri'] = spec.homepage
|
18
|
+
spec.metadata['changelog_uri'] = "#{spec.homepage}/blob/master/CHANGELOG.md"
|
19
|
+
|
20
|
+
# Specify which files should be added to the gem when it is released.
|
21
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
22
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
23
|
+
`git ls-files -z`.split("\x0").reject do |f|
|
24
|
+
(f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
|
25
|
+
end
|
26
|
+
end
|
27
|
+
spec.bindir = 'exe'
|
28
|
+
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
29
|
+
spec.require_paths = ['lib']
|
30
|
+
|
31
|
+
spec.add_dependency 'jekyll', '~> 4.0'
|
32
|
+
spec.add_dependency 'mini_magick', '~> 4.1'
|
33
|
+
spec.add_dependency 'toml', '~> 0.3.0'
|
34
|
+
|
35
|
+
spec.add_development_dependency 'rubocop'
|
36
|
+
end
|
@@ -0,0 +1,386 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'jekyll'
|
4
|
+
require 'mini_magick'
|
5
|
+
require 'pathname'
|
6
|
+
require 'toml'
|
7
|
+
|
8
|
+
module ImageGallery
|
9
|
+
class GalleryGenerator < Jekyll::Generator
|
10
|
+
safe true
|
11
|
+
|
12
|
+
IGNORED_FILES = ['.ds_store', 'thumbs.db'].freeze
|
13
|
+
|
14
|
+
GalleryImageModel = Struct.new(:path, :thumbnail_path, :creation_datetime, :original_path) do
|
15
|
+
def to_liquid
|
16
|
+
to_h.transform_keys(&:to_s)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
GalleryModel = Struct.new(:id, :name, :images, :highlight_image, :datetime) do
|
21
|
+
def to_liquid
|
22
|
+
to_h.map do |k, v|
|
23
|
+
if k == :datetime
|
24
|
+
[k.to_s, { 'year' => v.year, 'month' => v.month, 'day' => v.day }]
|
25
|
+
elsif v.class.method_defined?(:to_liquid)
|
26
|
+
[k.to_s, v.to_liquid]
|
27
|
+
else
|
28
|
+
[k.to_s, v]
|
29
|
+
end
|
30
|
+
end.to_h
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def generate(site)
|
35
|
+
galleries_dir = File.join(site.source, '_galleries')
|
36
|
+
unless File.directory?(galleries_dir)
|
37
|
+
ImageGallery._log(:warn, "No directory found at #{galleries_dir}, skipping gallery generation")
|
38
|
+
return
|
39
|
+
end
|
40
|
+
|
41
|
+
all_galleries = Dir.chdir(galleries_dir) do
|
42
|
+
Dir.glob('**/*/')
|
43
|
+
end
|
44
|
+
|
45
|
+
galleries = []
|
46
|
+
all_galleries.each do |gallery_path|
|
47
|
+
gallery_path_abs = File.join(site.source, '_galleries', gallery_path)
|
48
|
+
|
49
|
+
source_images = Dir.children(gallery_path_abs)
|
50
|
+
.select { |file_name| File.file?(File.join(gallery_path_abs, file_name)) }
|
51
|
+
.reject { |file_name| File.basename(file_name, '.*') == '_metadata' }
|
52
|
+
.reject { |file_name| IGNORED_FILES.include?(file_name.downcase) }
|
53
|
+
|
54
|
+
next if source_images.empty?
|
55
|
+
|
56
|
+
gallery_path_rel = File.join('_galleries', gallery_path)
|
57
|
+
|
58
|
+
gallery_images_and_times = source_images.map do |src_image_file_name|
|
59
|
+
src_image_file_path = File.join(gallery_path_rel, src_image_file_name)
|
60
|
+
|
61
|
+
# TODO: this assumes the image at a path doesn't change
|
62
|
+
exif = exif_cache.getset(src_image_file_path) do
|
63
|
+
ImageGallery._log_once(:info, 'Loading EXIF data. This may take some time on the first run')
|
64
|
+
MiniMagick::Image.open(src_image_file_path).exif
|
65
|
+
end
|
66
|
+
image_time = DateTime.strptime(exif['DateTimeOriginal'], '%Y:%m:%d %H:%M:%S') if exif['DateTimeOriginal']
|
67
|
+
|
68
|
+
{
|
69
|
+
path: src_image_file_path,
|
70
|
+
creation_datetime: image_time,
|
71
|
+
}
|
72
|
+
end
|
73
|
+
|
74
|
+
gallery = construct_gallery(gallery_path_abs, gallery_images_and_times)
|
75
|
+
gallery.images = copy_images(site, gallery, gallery_path_rel, gallery_images_and_times).sort_by do |gi|
|
76
|
+
if gi.creation_datetime.nil?
|
77
|
+
1.0 / 0.0
|
78
|
+
else
|
79
|
+
gi.creation_datetime
|
80
|
+
end
|
81
|
+
end.reverse
|
82
|
+
|
83
|
+
gallery.highlight_image = gallery.images.detect do |gi|
|
84
|
+
basename = File.basename(gi.original_path, '.*')
|
85
|
+
basename.start_with?('hl_') || basename.end_with?('_hl')
|
86
|
+
end || gallery.images[0]
|
87
|
+
|
88
|
+
galleries << gallery
|
89
|
+
site.pages << GalleryPage.new(site, gallery)
|
90
|
+
end
|
91
|
+
|
92
|
+
galleries = galleries.sort_by(&:datetime)
|
93
|
+
galleries.reverse!
|
94
|
+
|
95
|
+
galleries.group_by { |gallery| gallery.datetime.year }.each do |year, _galleries|
|
96
|
+
site.pages << GalleryIndexPage.new(site, year)
|
97
|
+
end
|
98
|
+
|
99
|
+
site.data['galleries'] = galleries
|
100
|
+
site.data['gallery_years'] = galleries.group_by { |gallery| gallery.datetime.year }.keys.sort
|
101
|
+
site.data['max_gallery_year'] = site.data['gallery_years'].max
|
102
|
+
|
103
|
+
if ImageGallery._config_with_defaults(site)['generate_root_index']
|
104
|
+
site.pages << GalleryIndexPage.new(
|
105
|
+
site,
|
106
|
+
site.data['max_gallery_year'],
|
107
|
+
override_dir: File.join(*ImageGallery.gallery_path_array(site))
|
108
|
+
)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def construct_gallery(gallery_path_abs, gallery_images)
|
113
|
+
gallery = {}
|
114
|
+
|
115
|
+
_ancestors, gallery_id = Pathname.new(gallery_path_abs).split
|
116
|
+
gallery_id = gallery_id.to_s
|
117
|
+
|
118
|
+
gallery_regex = /^(\d{4})[-_]?(\d{2})[-_]?(\d{2})[-_]?(.*)$/
|
119
|
+
gallery_dir_match = gallery_id.match(gallery_regex)
|
120
|
+
if gallery_dir_match
|
121
|
+
gallery['id'] = gallery_dir_match[4]
|
122
|
+
gallery['name'] = gallery_dir_match[4].split(/[_-]+/).map(&:capitalize).join(' ')
|
123
|
+
gallery['datetime'] = DateTime.new(gallery_dir_match[1].to_i, gallery_dir_match[2].to_i, gallery_dir_match[3].to_i)
|
124
|
+
else
|
125
|
+
ImageGallery._log(:debug, "Gallery name #{gallery_id} doesn't match the expected regex #{gallery_regex}")
|
126
|
+
gallery['id'] = gallery_id
|
127
|
+
gallery['name'] = gallery_id.split(/[_-]+/).map(&:capitalize).join(' ')
|
128
|
+
end
|
129
|
+
|
130
|
+
earliest_image = gallery_images.reject { |gi| gi[:creation_datetime].nil? }.min { |gi| gi[:creation_datetime] }
|
131
|
+
gallery['datetime'] = earliest_image[:creation_datetime] if earliest_image
|
132
|
+
|
133
|
+
file_metadata = load_metadata_from_dir(gallery_path_abs)
|
134
|
+
gallery = gallery.merge(transform_metadata(file_metadata)) if file_metadata
|
135
|
+
|
136
|
+
if gallery['datetime'].nil?
|
137
|
+
raise "Gallery at #{gallery_path_abs} has no datetime specified - it needs to be in EXIF data, the gallery prefix or the _metadata.json file"
|
138
|
+
end
|
139
|
+
|
140
|
+
GalleryModel.new(*gallery.transform_keys(&:to_sym).values_at(*GalleryModel.members))
|
141
|
+
end
|
142
|
+
|
143
|
+
def load_metadata_from_dir(path)
|
144
|
+
ext_to_parsers = [
|
145
|
+
['toml', ->(f) { TOML.load_file(f) }],
|
146
|
+
['json', ->(f) { JSON.parse(File.new(f)) }],
|
147
|
+
]
|
148
|
+
|
149
|
+
path_to_parsers = ext_to_parsers.map do |(ext, fn)|
|
150
|
+
full_path = File.join(path, "_metadata.#{ext}")
|
151
|
+
[full_path, fn]
|
152
|
+
end
|
153
|
+
first_existing_file = path_to_parsers.find do |(full_path, _fn)|
|
154
|
+
File.file?(full_path)
|
155
|
+
end
|
156
|
+
|
157
|
+
if first_existing_file
|
158
|
+
full_path, fn = first_existing_file
|
159
|
+
fn.call(full_path)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
def transform_metadata(raw_data)
|
164
|
+
raw_data.map do |k, v|
|
165
|
+
if k == 'datetime'
|
166
|
+
[k, DateTime.parse(v)]
|
167
|
+
else
|
168
|
+
[k, v]
|
169
|
+
end
|
170
|
+
end.to_h
|
171
|
+
end
|
172
|
+
|
173
|
+
def copy_images(site, gallery, gallery_path_rel, gallery_images_and_times)
|
174
|
+
image_directory = File.join(*ImageGallery.gallery_path_array(site, gallery.datetime.year, gallery.datetime.month))
|
175
|
+
|
176
|
+
gallery_images_and_times.map do |img_and_time|
|
177
|
+
src_image_file_path = img_and_time[:path]
|
178
|
+
src_image_file_name = File.basename(src_image_file_path)
|
179
|
+
sha = Digest::SHA256.hexdigest(src_image_file_path)
|
180
|
+
image_file_name = "#{sha}#{File.extname(src_image_file_path)}"
|
181
|
+
thumb_file_name = "#{sha}_t#{File.extname(src_image_file_path)}"
|
182
|
+
|
183
|
+
config = ImageGallery._config_with_defaults(site)
|
184
|
+
|
185
|
+
image_file = CompressedStaticGalleryFile.new(site, site.source, gallery_path_rel, src_image_file_name, image_directory, image_file_name) do |i|
|
186
|
+
if config['image_size']
|
187
|
+
i.resize("#{config['image_size']['x']}x#{config['image_size']['y']}>")
|
188
|
+
ImageGallery._log(:info, "Processing image #{src_image_file_path} to extents #{config['image_size']}")
|
189
|
+
else
|
190
|
+
ImageGallery._log(:info, "Processing image #{src_image_file_path}")
|
191
|
+
end
|
192
|
+
end
|
193
|
+
thumb_file = CompressedStaticGalleryFile.new(site, site.source, gallery_path_rel, src_image_file_name, image_directory, thumb_file_name) do |i|
|
194
|
+
i.gravity('Center')
|
195
|
+
i.resize("#{config['thumbnail_size']['x']}x#{config['thumbnail_size']['y']}^")
|
196
|
+
i.extent("#{config['thumbnail_size']['x']}x#{config['thumbnail_size']['y']}")
|
197
|
+
ImageGallery._log(:info, "Processing image #{src_image_file_path} to #{config['thumbnail_size']}")
|
198
|
+
end
|
199
|
+
|
200
|
+
site.static_files.push(image_file).push(thumb_file)
|
201
|
+
|
202
|
+
GalleryImageModel.new(
|
203
|
+
File.join('/', image_file.relative_destination),
|
204
|
+
File.join('/', thumb_file.relative_destination),
|
205
|
+
img_and_time[:creation_datetime],
|
206
|
+
src_image_file_path
|
207
|
+
)
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
def exif_cache
|
212
|
+
@@exif_cache ||= Jekyll::Cache.new('jekyll-image-gallery::exif')
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
class GalleryPage < Jekyll::Page
|
217
|
+
def initialize(site, gallery)
|
218
|
+
@site = site
|
219
|
+
@base = site.source
|
220
|
+
|
221
|
+
year = gallery.datetime.year
|
222
|
+
month = gallery.datetime.month
|
223
|
+
@dir = File.join(*ImageGallery.gallery_path_array(site, year, month))
|
224
|
+
|
225
|
+
@basename = gallery.id
|
226
|
+
@ext = '.html'
|
227
|
+
@name = "#{@basename}#{@ext}"
|
228
|
+
|
229
|
+
config = ImageGallery._config_with_defaults(site)
|
230
|
+
@data = {
|
231
|
+
'layout' => 'gallery_page',
|
232
|
+
'gallery' => gallery,
|
233
|
+
'year' => year,
|
234
|
+
'title' => [config['title_prefix'], year, gallery.name].join(" #{config['title_seperator']} "),
|
235
|
+
}
|
236
|
+
|
237
|
+
data.default_proc = proc do |_, key|
|
238
|
+
site.frontmatter_defaults.find(relative_path, :galleries, key)
|
239
|
+
end
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
class GalleryIndexPage < Jekyll::Page
|
244
|
+
def initialize(site, year, override_dir: nil)
|
245
|
+
@site = site
|
246
|
+
@base = site.source
|
247
|
+
@dir = override_dir || File.join(*ImageGallery.gallery_path_array(site, year))
|
248
|
+
|
249
|
+
@basename = 'index'
|
250
|
+
@ext = '.html'
|
251
|
+
@name = "#{@basename}#{@ext}"
|
252
|
+
|
253
|
+
config = ImageGallery._config_with_defaults(site)
|
254
|
+
@data = {
|
255
|
+
'layout' => 'gallery_index',
|
256
|
+
'year' => year,
|
257
|
+
'title' => [config['title_prefix'], year].join(" #{config['title_seperator']} "),
|
258
|
+
}
|
259
|
+
|
260
|
+
data.default_proc = proc do |_, key|
|
261
|
+
site.frontmatter_defaults.find(relative_path, :gallery_indexes, key)
|
262
|
+
end
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
class CompressedStaticGalleryFile < Jekyll::StaticFile
|
267
|
+
BROWSER_SUPPORTED_EXTENSIONS = ['.png', '.jpeg', '.jpg'].freeze
|
268
|
+
FALLBACK_FORMAT = 'png'.freeze
|
269
|
+
|
270
|
+
def initialize(site, base, source_path, source_name, dest_path, dest_name, &resize_fn)
|
271
|
+
super(site, base, source_path, source_name)
|
272
|
+
@dest_path = dest_path
|
273
|
+
@dest_name = dest_name
|
274
|
+
@resize_fn = resize_fn
|
275
|
+
|
276
|
+
# Apples HEIC format isn't supported by browsers. Using filename isn't an ideal heuristic, but it is probably good
|
277
|
+
# enough
|
278
|
+
@convert_format = false
|
279
|
+
unless BROWSER_SUPPORTED_EXTENSIONS.include?(File.extname(@dest_name).downcase)
|
280
|
+
@convert_format = true
|
281
|
+
@dest_name = "#{File.basename(@dest_name, '.*')}.#{FALLBACK_FORMAT}"
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
def destination(dest)
|
286
|
+
File.join(dest, @dest_path, @dest_name)
|
287
|
+
end
|
288
|
+
|
289
|
+
def relative_destination
|
290
|
+
File.join(@dest_path, @dest_name)
|
291
|
+
end
|
292
|
+
|
293
|
+
def write(dest)
|
294
|
+
dest_path = destination(dest)
|
295
|
+
|
296
|
+
return false if File.exist?(dest_path) && (File.stat(path).mtime.to_i == File.stat(dest_path).mtime.to_i)
|
297
|
+
|
298
|
+
FileUtils.mkdir_p(File.dirname(dest_path))
|
299
|
+
FileUtils.rm(dest_path) if File.exist?(dest_path)
|
300
|
+
|
301
|
+
image = MiniMagick::Image.open(path)
|
302
|
+
image.combine_options do |i|
|
303
|
+
i.auto_orient
|
304
|
+
i.strip
|
305
|
+
@resize_fn.call(i)
|
306
|
+
end
|
307
|
+
image.format FALLBACK_FORMAT if @convert_format
|
308
|
+
image.write(dest_path)
|
309
|
+
|
310
|
+
File.utime(File.atime(dest_path), mtime, dest_path)
|
311
|
+
|
312
|
+
true
|
313
|
+
end
|
314
|
+
end
|
315
|
+
|
316
|
+
def self._config_with_defaults(site)
|
317
|
+
defaults = {
|
318
|
+
'path' => 'gallery',
|
319
|
+
'generate_root_index' => false,
|
320
|
+
'thumbnail_size' => {
|
321
|
+
'x' => 512,
|
322
|
+
'y' => 512,
|
323
|
+
},
|
324
|
+
"title_prefix" => 'Photos',
|
325
|
+
"title_seperator" => '|',
|
326
|
+
}
|
327
|
+
|
328
|
+
config = site.config
|
329
|
+
unless config
|
330
|
+
ImageGallery._log_once(:warn, 'No configuration (site.config) found for jekyll-image-gallery, defaults will be used')
|
331
|
+
return defaults
|
332
|
+
end
|
333
|
+
|
334
|
+
gallery_config = config['gallery']
|
335
|
+
unless gallery_config
|
336
|
+
ImageGallery._log_once(:warn, 'No configuration found for jekyll-image-gallery, defaults will be used')
|
337
|
+
return defaults
|
338
|
+
end
|
339
|
+
|
340
|
+
defaults.merge(gallery_config)
|
341
|
+
end
|
342
|
+
|
343
|
+
def self._log(level, message)
|
344
|
+
Jekyll.logger.public_send(level, "\tjekyll-image-gallery: #{message}")
|
345
|
+
end
|
346
|
+
|
347
|
+
def self._log_once(level, message)
|
348
|
+
@@seen_log_messages ||= {}
|
349
|
+
return if @@seen_log_messages.include?(message)
|
350
|
+
|
351
|
+
@@seen_log_messages[message] = true
|
352
|
+
ImageGallery._log(level, message)
|
353
|
+
end
|
354
|
+
|
355
|
+
def self.gallery_path_array(site, year = nil, month = nil)
|
356
|
+
config = ImageGallery._config_with_defaults(site)
|
357
|
+
|
358
|
+
gallery_path = []
|
359
|
+
gallery_path = config['path'].split('/') if config['path']
|
360
|
+
|
361
|
+
gallery_path.push(year.to_s) if year
|
362
|
+
|
363
|
+
if month
|
364
|
+
padded_month = format('%02d', month)
|
365
|
+
gallery_path.push(padded_month)
|
366
|
+
end
|
367
|
+
|
368
|
+
gallery_path
|
369
|
+
end
|
370
|
+
|
371
|
+
module Filters
|
372
|
+
def gallery_url(gallery)
|
373
|
+
site = @context.registers[:site]
|
374
|
+
creation_time = gallery['datetime']
|
375
|
+
gallery_path = ImageGallery.gallery_path_array(site, creation_time['year'], creation_time['month']).join('/')
|
376
|
+
"/#{gallery_path}/#{gallery['id']}.html"
|
377
|
+
end
|
378
|
+
|
379
|
+
def gallery_index_url(year)
|
380
|
+
site = @context.registers[:site]
|
381
|
+
"/#{ImageGallery.gallery_path_array(site, year).join('/')}/"
|
382
|
+
end
|
383
|
+
end
|
384
|
+
end
|
385
|
+
|
386
|
+
Liquid::Template.register_filter(ImageGallery::Filters)
|
metadata
ADDED
@@ -0,0 +1,112 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: jekyll-image-gallery
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jacob Essex
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2022-09-13 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: jekyll
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '4.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '4.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: mini_magick
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '4.1'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '4.1'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: toml
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 0.3.0
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 0.3.0
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rubocop
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
description:
|
70
|
+
email:
|
71
|
+
- ruby_gems@JacobEssex.com
|
72
|
+
executables: []
|
73
|
+
extensions: []
|
74
|
+
extra_rdoc_files: []
|
75
|
+
files:
|
76
|
+
- ".rubocop.yml"
|
77
|
+
- CHANGELOG.md
|
78
|
+
- Gemfile
|
79
|
+
- LICENSE.txt
|
80
|
+
- README.md
|
81
|
+
- Rakefile
|
82
|
+
- jekyll-image-gallery.gemspec
|
83
|
+
- lib/gallery/gallery.rb
|
84
|
+
- lib/gallery/version.rb
|
85
|
+
- lib/jekyll-image-gallery.rb
|
86
|
+
homepage: https://github.com/Yacoby/jekyll-image-gallery
|
87
|
+
licenses:
|
88
|
+
- MIT
|
89
|
+
metadata:
|
90
|
+
homepage_uri: https://github.com/Yacoby/jekyll-image-gallery
|
91
|
+
source_code_uri: https://github.com/Yacoby/jekyll-image-gallery
|
92
|
+
changelog_uri: https://github.com/Yacoby/jekyll-image-gallery/blob/master/CHANGELOG.md
|
93
|
+
post_install_message:
|
94
|
+
rdoc_options: []
|
95
|
+
require_paths:
|
96
|
+
- lib
|
97
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
98
|
+
requirements:
|
99
|
+
- - ">="
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: 2.6.0
|
102
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
103
|
+
requirements:
|
104
|
+
- - ">="
|
105
|
+
- !ruby/object:Gem::Version
|
106
|
+
version: '0'
|
107
|
+
requirements: []
|
108
|
+
rubygems_version: 3.3.7
|
109
|
+
signing_key:
|
110
|
+
specification_version: 4
|
111
|
+
summary: Gallery generator for Jekyll
|
112
|
+
test_files: []
|