m1key_gallery_generator 0.3.2
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.
- checksums.yaml +7 -0
- data/.gitignore +38 -0
- data/.rspec +2 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +123 -0
- data/Rakefile +9 -0
- data/bin/console +57 -0
- data/bin/setup +7 -0
- data/lib/m1key_gallery_generator/console_utils.rb +6 -0
- data/lib/m1key_gallery_generator/gallery_config.rb +58 -0
- data/lib/m1key_gallery_generator/gallery_mutators.rb +34 -0
- data/lib/m1key_gallery_generator/photos_utils.rb +51 -0
- data/lib/m1key_gallery_generator/string_utils.rb +19 -0
- data/lib/m1key_gallery_generator/template.erb +129 -0
- data/lib/m1key_gallery_generator/version.rb +3 -0
- data/lib/m1key_gallery_generator/viewable_gallery.rb +65 -0
- data/lib/m1key_gallery_generator/viewable_photo.rb +24 -0
- data/lib/m1key_gallery_generator/viewable_photo_metadata.rb +14 -0
- data/lib/m1key_gallery_generator.rb +6 -0
- data/m1key_gallery_generator.gemspec +30 -0
- metadata +138 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 49662e64799771b65adb4f6b2dc9eef86fdf0e8e21817e4c4435fe5b5b9b375e
|
|
4
|
+
data.tar.gz: 7a03a418ce8c31d997b1da617fc655a7f74cdb1ea82c71f2e4d436300c2472b5
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 6c95eafb5cc4b78f2f5831ff417100cfd704a56f02f0055583a8f6742df14f79765a2f6fb80035aa776f53ce60edc0bf9bfef9ed4b7ba3017518d31f0ebdc007
|
|
7
|
+
data.tar.gz: b2224404d3ba85456f83f275727fac380d2175c0425d9b66f07d86184e8b8002a184a5b2ef44581f2c0391157df110e98515c7212fd0e498a2d40195c4200d03
|
data/.gitignore
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
index.html
|
|
2
|
+
uzbekistan*
|
|
3
|
+
*.gem
|
|
4
|
+
*.rbc
|
|
5
|
+
/.config
|
|
6
|
+
/coverage/
|
|
7
|
+
/InstalledFiles
|
|
8
|
+
/pkg/
|
|
9
|
+
/spec/reports/
|
|
10
|
+
/test/tmp/
|
|
11
|
+
/test/version_tmp/
|
|
12
|
+
/tmp/
|
|
13
|
+
.idea
|
|
14
|
+
*.iml
|
|
15
|
+
.rakeTasks
|
|
16
|
+
vendor
|
|
17
|
+
|
|
18
|
+
## Specific to RubyMotion:
|
|
19
|
+
.dat*
|
|
20
|
+
.repl_history
|
|
21
|
+
build/
|
|
22
|
+
|
|
23
|
+
## Documentation cache and generated files:
|
|
24
|
+
/.yardoc/
|
|
25
|
+
/_yardoc/
|
|
26
|
+
/doc/
|
|
27
|
+
/rdoc/
|
|
28
|
+
|
|
29
|
+
## Environment normalisation:
|
|
30
|
+
/.bundle/
|
|
31
|
+
/lib/bundler/man/
|
|
32
|
+
|
|
33
|
+
Gemfile.lock
|
|
34
|
+
.ruby-version
|
|
35
|
+
.ruby-gemset
|
|
36
|
+
|
|
37
|
+
# unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
|
|
38
|
+
.rvmrc
|
data/.rspec
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2015 Michal Huniewicz
|
|
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,123 @@
|
|
|
1
|
+
# M1keyGalleryGenerator
|
|
2
|
+
|
|
3
|
+
This Ruby script generates www.m1key.me style gallery.
|
|
4
|
+
|
|
5
|
+
A sample gallery is this: https://www.m1key.me/photography/mauritania_part_2/
|
|
6
|
+
|
|
7
|
+
## Status
|
|
8
|
+
|
|
9
|
+
This is work in progress, but has been used to generate real-life galleries.
|
|
10
|
+
|
|
11
|
+
## Set up
|
|
12
|
+
|
|
13
|
+
### Fedora 35+
|
|
14
|
+
|
|
15
|
+
Install Ruby:
|
|
16
|
+
|
|
17
|
+
> dnf install ruby
|
|
18
|
+
|
|
19
|
+
Install Ruby Gem Bundler in Fedora 35 and higher:
|
|
20
|
+
|
|
21
|
+
> gem install bundler
|
|
22
|
+
|
|
23
|
+
> dnf install ruby-irb
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
### Ubuntu
|
|
27
|
+
|
|
28
|
+
Install Ruby using rbenv. Then:
|
|
29
|
+
|
|
30
|
+
> rbenv install 3.4.7
|
|
31
|
+
|
|
32
|
+
> rbenv global 3.4.7
|
|
33
|
+
|
|
34
|
+
> gem install bundler
|
|
35
|
+
|
|
36
|
+
### Windows
|
|
37
|
+
|
|
38
|
+
Install Ruby. This has been most recently tested on Ruby 3.4.7.
|
|
39
|
+
|
|
40
|
+
Install Gitbash.
|
|
41
|
+
|
|
42
|
+
> gem install bundler
|
|
43
|
+
|
|
44
|
+
### All
|
|
45
|
+
|
|
46
|
+
Install dependencies:
|
|
47
|
+
|
|
48
|
+
> ./bin/setup
|
|
49
|
+
|
|
50
|
+
## Tests
|
|
51
|
+
|
|
52
|
+
Run the script on some test data. This is required to run other tests.
|
|
53
|
+
This assumes there is a valid gallery.yaml file present in the current folder,
|
|
54
|
+
and the photos themselves in the format DSC_1234.jpg.
|
|
55
|
+
*This is required to run tests.*
|
|
56
|
+
|
|
57
|
+
> ruby -Ilib bin/console test/data
|
|
58
|
+
|
|
59
|
+
Run other tests:
|
|
60
|
+
|
|
61
|
+
> rake
|
|
62
|
+
|
|
63
|
+
> rspec spec
|
|
64
|
+
|
|
65
|
+
## Usage
|
|
66
|
+
|
|
67
|
+
This assumes you are running Windows.
|
|
68
|
+
|
|
69
|
+
Clone the project.
|
|
70
|
+
|
|
71
|
+
Build the gem.
|
|
72
|
+
|
|
73
|
+
> gem build m1key_gallery_generator.gemspec
|
|
74
|
+
|
|
75
|
+
Install the gem.
|
|
76
|
+
|
|
77
|
+
> gem install m1key_gallery_generator-0.3.2.gem
|
|
78
|
+
|
|
79
|
+
In your gallery directory, have a file called generate.bat that looks like this:
|
|
80
|
+
|
|
81
|
+
> ruby c:\Ruby34-x64\lib\ruby\gems\3.4.0\gems\m1key_gallery_generator-0.3.2\bin\console . wait_on_error
|
|
82
|
+
|
|
83
|
+
## Development
|
|
84
|
+
|
|
85
|
+
### Release
|
|
86
|
+
|
|
87
|
+
To release a new version, update the version number in `version.rb`,
|
|
88
|
+
commit your changes,
|
|
89
|
+
and then run `bundle exec rake release` to create a git tag for the version,
|
|
90
|
+
push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
|
91
|
+
|
|
92
|
+
### Gem
|
|
93
|
+
|
|
94
|
+
To build gem:
|
|
95
|
+
> gem build m1key_gallery_generator.gemspec
|
|
96
|
+
|
|
97
|
+
To install gem:
|
|
98
|
+
> gem install m1key_gallery_generator-0.3.2.gem
|
|
99
|
+
|
|
100
|
+
To test gem:
|
|
101
|
+
> irb
|
|
102
|
+
|
|
103
|
+
> require 'm1key_gallery_generator'
|
|
104
|
+
|
|
105
|
+
### Installation
|
|
106
|
+
|
|
107
|
+
Add this line to your application's Gemfile:
|
|
108
|
+
|
|
109
|
+
```ruby
|
|
110
|
+
gem 'm1key_gallery_generator'
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
And then execute:
|
|
114
|
+
|
|
115
|
+
$ bundle
|
|
116
|
+
|
|
117
|
+
Or install it yourself as:
|
|
118
|
+
|
|
119
|
+
$ gem install m1key_gallery_generator
|
|
120
|
+
|
|
121
|
+
## Gotchas
|
|
122
|
+
|
|
123
|
+
Do not edit the templates in Atom due to its whitespace magic. I used vi instead.
|
data/Rakefile
ADDED
data/bin/console
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
require 'm1key_gallery_generator'
|
|
4
|
+
require 'erb'
|
|
5
|
+
|
|
6
|
+
Encoding.default_external = Encoding::UTF_8
|
|
7
|
+
|
|
8
|
+
include GalleryGenerator
|
|
9
|
+
|
|
10
|
+
OUTPUT_FILE = 'index.html'
|
|
11
|
+
DEFAULT_GALLERY_CONFIG_FILE = 'gallery.yaml'
|
|
12
|
+
|
|
13
|
+
puts "M1key.me Gallery Generator #{M1keyGalleryGenerator::VERSION}"
|
|
14
|
+
puts 'www.m1key.me'
|
|
15
|
+
puts 'This generates a m1key.me style gallery HTML code.'
|
|
16
|
+
puts
|
|
17
|
+
|
|
18
|
+
working_directory = if ARGV.empty?
|
|
19
|
+
Dir.pwd.tap { |dir| puts "Working directory not specified. Using [#{dir}]." }
|
|
20
|
+
else
|
|
21
|
+
ARGV[0].tap { |dir| puts "Using working directory [#{dir}]." }
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
puts
|
|
25
|
+
|
|
26
|
+
begin
|
|
27
|
+
gallery_config = GalleryConfig.new(File.join(working_directory, DEFAULT_GALLERY_CONFIG_FILE))
|
|
28
|
+
viewable_photos = photos_config_into_viewable_photos(working_directory, gallery_config)
|
|
29
|
+
|
|
30
|
+
puts 'Creating viewable gallery...'
|
|
31
|
+
|
|
32
|
+
gallery = ViewableGallery.new(gallery_config.title, gallery_config.description, gallery_config.slug,
|
|
33
|
+
gallery_config.sources, gallery_config.upload_date, gallery_config.map_url, gallery_config.map_title,
|
|
34
|
+
gallery_config.year, viewable_photos, gallery_config.small_print).
|
|
35
|
+
update_using(
|
|
36
|
+
add_tabs_before_every_description_line(3),
|
|
37
|
+
add_links_to_descriptions,
|
|
38
|
+
for_each_photo(&add_links_to_descriptions),
|
|
39
|
+
for_each_photo(&remove_final_empty_line_from_description))
|
|
40
|
+
|
|
41
|
+
puts 'Preparing to write the output...'
|
|
42
|
+
|
|
43
|
+
output_file = File.join(working_directory, OUTPUT_FILE)
|
|
44
|
+
puts "Writing gallery file #{output_file}..."
|
|
45
|
+
template_file = File.read("#{File.dirname(File.expand_path(__FILE__))}/../lib/m1key_gallery_generator/template.erb")
|
|
46
|
+
erb = ERB.new(template_file, trim_mode: '-')
|
|
47
|
+
File.write(output_file, erb.result(gallery.get_binding))
|
|
48
|
+
|
|
49
|
+
puts "#{output_file} written."
|
|
50
|
+
|
|
51
|
+
rescue StandardError => e
|
|
52
|
+
puts e
|
|
53
|
+
if ARGV.length > 1 && ARGV[1] == 'wait_on_error'
|
|
54
|
+
require 'io/console'
|
|
55
|
+
STDIN.getch
|
|
56
|
+
end
|
|
57
|
+
end
|
data/bin/setup
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
require 'date'
|
|
2
|
+
require 'yaml'
|
|
3
|
+
require_relative 'console_utils'
|
|
4
|
+
|
|
5
|
+
module GalleryGenerator
|
|
6
|
+
class GalleryConfig
|
|
7
|
+
|
|
8
|
+
attr_reader :title, :map_url, :map_title, :slug, :upload_date, :description, :sources, :year, :photos, :small_print
|
|
9
|
+
|
|
10
|
+
def initialize(yaml_config_file_name)
|
|
11
|
+
puts "Attempting to parse #{yaml_config_file_name} to read gallery configuration..."
|
|
12
|
+
gallery_configuration = YAML.safe_load_file(yaml_config_file_name, permitted_classes: [Date, Symbol], permitted_symbols: [])
|
|
13
|
+
puts 'Parsed, seemingly.'
|
|
14
|
+
puts
|
|
15
|
+
|
|
16
|
+
@title = gallery_configuration['title']
|
|
17
|
+
puts "Gallery title is [#{@title}]."
|
|
18
|
+
@map_url = gallery_configuration['map']['url']
|
|
19
|
+
puts "Map url is [#{@map_url}]."
|
|
20
|
+
@map_title = gallery_configuration['map']['title']
|
|
21
|
+
puts "Map title is [#{@map_title}]."
|
|
22
|
+
@slug = gallery_configuration['slug']
|
|
23
|
+
puts "Gallery slug is [#{@slug}]."
|
|
24
|
+
@upload_date = gallery_configuration['upload_date']
|
|
25
|
+
puts "Gallery upload date is [#{@upload_date}]."
|
|
26
|
+
@description = gallery_configuration['description']
|
|
27
|
+
puts "Gallery description is [#{compact(@description)}]."
|
|
28
|
+
@small_print = gallery_configuration['small_print']
|
|
29
|
+
puts "Small print is [#{@small_print}]."
|
|
30
|
+
|
|
31
|
+
@sources = gallery_configuration['sources']
|
|
32
|
+
|
|
33
|
+
gallery_year_from_yaml = gallery_configuration['year']
|
|
34
|
+
if gallery_year_from_yaml == 'current'
|
|
35
|
+
puts 'Using current year for gallery year...'
|
|
36
|
+
current_year = Date.today.strftime('%Y')
|
|
37
|
+
@year = Integer(current_year)
|
|
38
|
+
else
|
|
39
|
+
@year = Integer(gallery_year_from_yaml)
|
|
40
|
+
unless @year > 999 and @year < 10000
|
|
41
|
+
abort 'Gallery year should be exactly four digits long.'
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
puts "Gallery year is [#{year}]."
|
|
45
|
+
puts
|
|
46
|
+
|
|
47
|
+
@photos = gallery_configuration['photos']
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def total_photos_number
|
|
51
|
+
@photos.size
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def photo_id_digits
|
|
55
|
+
[2, total_photos_number.to_s.size].max
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
require_relative 'string_utils'
|
|
2
|
+
|
|
3
|
+
module GalleryGenerator
|
|
4
|
+
def add_tabs_before_every_description_line(how_many_tabs)
|
|
5
|
+
lambda do |mutable_viewable_content|
|
|
6
|
+
mutable_viewable_content.description = add_tabs_before_every_line(mutable_viewable_content.description, how_many_tabs)
|
|
7
|
+
mutable_viewable_content
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def add_links_to_descriptions
|
|
12
|
+
lambda do |mutable_viewable_content|
|
|
13
|
+
mutable_viewable_content.description = add_links_to_sources(mutable_viewable_content.description)
|
|
14
|
+
mutable_viewable_content
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def remove_final_empty_line_from_description
|
|
19
|
+
lambda do |mutable_viewable_content|
|
|
20
|
+
mutable_viewable_content.description = remove_final_empty_line(mutable_viewable_content.description)
|
|
21
|
+
mutable_viewable_content
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def for_each_photo(&update_function)
|
|
26
|
+
lambda do |mutable_viewable_gallery|
|
|
27
|
+
mutable_viewable_gallery.photos.each do |mutable_viewable_photo|
|
|
28
|
+
update_function.call(mutable_viewable_photo)
|
|
29
|
+
end
|
|
30
|
+
mutable_viewable_gallery
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
require 'exifr/jpeg'
|
|
2
|
+
require 'fileutils'
|
|
3
|
+
require_relative 'viewable_photo_metadata'
|
|
4
|
+
|
|
5
|
+
module GalleryGenerator
|
|
6
|
+
|
|
7
|
+
def photos_config_into_viewable_photos(working_directory, gallery_config)
|
|
8
|
+
photos = []
|
|
9
|
+
current_photo_number = 0
|
|
10
|
+
total_photos_number = gallery_config.total_photos_number
|
|
11
|
+
photo_id_digits = gallery_config.photo_id_digits
|
|
12
|
+
puts "There are [#{total_photos_number}] photos total."
|
|
13
|
+
gallery_config.photos.each do |photo|
|
|
14
|
+
current_photo_number += 1
|
|
15
|
+
photo_id = to_photo_id(current_photo_number, photo_id_digits)
|
|
16
|
+
photo_title = photo['title']
|
|
17
|
+
photo_description = photo['description']
|
|
18
|
+
photo_file_name_contains = photo['fileNameContains']
|
|
19
|
+
photo_technical_info = photo['technicalInfo']
|
|
20
|
+
photo_metadata = get_metadata_for_image_with_file_name_containing(working_directory, photo_file_name_contains)
|
|
21
|
+
|
|
22
|
+
puts "Adding photo with ID [#{photo_id}], title [#{photo_title}], height [#{photo_metadata.height}], description [#{compact(photo['description'])}]..."
|
|
23
|
+
photos.push ViewablePhoto.new(photo_id, photo_title, photo_description, photo_metadata, photo_technical_info)
|
|
24
|
+
|
|
25
|
+
create_gallery_image(photo_metadata.original_file_name, gallery_config.slug, photo_id, working_directory)
|
|
26
|
+
end
|
|
27
|
+
photos
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def to_photo_id(current_photo_number, photo_id_digits)
|
|
31
|
+
current_photo_number.to_s.rjust(photo_id_digits, '0')
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def get_metadata_for_image_with_file_name_containing(working_directory, file_name_contains)
|
|
35
|
+
selected_file_name = Dir.entries(working_directory).find { |file_name| file_name.include?(file_name_contains.to_s) }
|
|
36
|
+
|
|
37
|
+
raise "ERROR No matching photo found for #{file_name_contains}." unless selected_file_name
|
|
38
|
+
|
|
39
|
+
exif = EXIFR::JPEG.new(File.join(working_directory, selected_file_name))
|
|
40
|
+
photo_height = exif.height
|
|
41
|
+
photo_iso = exif.iso_speed_ratings
|
|
42
|
+
photo_focal_length = exif.focal_length.to_f.round.to_s
|
|
43
|
+
photo_f_number = exif.f_number.to_f
|
|
44
|
+
photo_exposure_time = exif.exposure_time.to_s
|
|
45
|
+
ViewablePhotoMetadata.new(selected_file_name, photo_height, photo_iso, photo_focal_length, photo_f_number, photo_exposure_time)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def create_gallery_image(original_file_name, gallery_slug, photo_id, working_directory)
|
|
49
|
+
FileUtils.cp(File.join(working_directory, original_file_name), File.join(working_directory, "#{gallery_slug}_#{photo_id}#{File.extname(original_file_name)}"))
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
module GalleryGenerator
|
|
2
|
+
def tabs(how_many_tabs)
|
|
3
|
+
" " * how_many_tabs
|
|
4
|
+
end
|
|
5
|
+
|
|
6
|
+
def add_tabs_before_every_line(multi_line_string, how_many_tabs)
|
|
7
|
+
multi_line_string.each_line.map { |line| tabs(how_many_tabs) + line }.join
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def remove_final_empty_line(multi_line_string)
|
|
11
|
+
lines = multi_line_string.lines
|
|
12
|
+
lines[-1] = lines[-1].sub(/[\r\n]$/, '') if lines.any?
|
|
13
|
+
lines.join
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def add_links_to_sources(multi_line_string)
|
|
17
|
+
multi_line_string.gsub(/\[(\d+)\]/, '[<a href="#sources">\1</a>]')
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
|
|
4
|
+
<head>
|
|
5
|
+
<meta charset="utf-8" />
|
|
6
|
+
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
|
7
|
+
<title><%= title %> — Photography and Art portfolio by Michal Huniewicz</title>
|
|
8
|
+
<meta name="description" content="<%= title %> — Photography and Art portfolio by Michal Huniewicz" />
|
|
9
|
+
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
10
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
11
|
+
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@800;900&display=swap" rel="stylesheet" />
|
|
12
|
+
<link href="https://fonts.googleapis.com/css2?family=Libre+Baskerville:wght@400;500;600;700&display=swap"
|
|
13
|
+
rel="stylesheet" />
|
|
14
|
+
<link rel="stylesheet" href="../../styles/base_responsive.css" />
|
|
15
|
+
<link rel="icon" href="../../images/icon.ico" />
|
|
16
|
+
|
|
17
|
+
<meta property="og:locale" content="en_US" />
|
|
18
|
+
<meta property="og:type" content="website" />
|
|
19
|
+
<meta property="og:title" content="<%= title %> by Michal Huniewicz" />
|
|
20
|
+
<meta property="og:description" content="<%= title %> — Photography and Art portfolio by Michal Huniewicz" />
|
|
21
|
+
<meta property="og:url" content="https://www.m1key.me/" />
|
|
22
|
+
<meta property="og:image" content="https://www.m1key.me/photography/<%= slug %>/<%= slug %>_01.jpg" />
|
|
23
|
+
<meta property="og:image:alt" content="<%= title %> by Michal Huniewicz" />
|
|
24
|
+
|
|
25
|
+
<meta name="twitter:card" content="summary_large_image" />
|
|
26
|
+
<meta name="twitter:title" content="<%= title %> by Michal Huniewicz" />
|
|
27
|
+
<meta name="twitter:description" content="<%= title %> — Photography and Art portfolio by Michal Huniewicz" />
|
|
28
|
+
<meta name="twitter:image" content="https://www.m1key.me/photography/<%= slug %>/<%= slug %>_01.jpg" />
|
|
29
|
+
|
|
30
|
+
<!-- Breadcrumb structured data for SEO -->
|
|
31
|
+
<script type="application/ld+json">
|
|
32
|
+
{
|
|
33
|
+
"@context": "https://schema.org",
|
|
34
|
+
"@type": "ImageGallery",
|
|
35
|
+
"name": "<%= title %> by Michal Huniewicz",
|
|
36
|
+
"url": "https://www.m1key.me/photography/<%= slug %>",
|
|
37
|
+
"author": {
|
|
38
|
+
"@type": "Person",
|
|
39
|
+
"name": "Michal Huniewicz"
|
|
40
|
+
},
|
|
41
|
+
"image": [
|
|
42
|
+
{
|
|
43
|
+
"@type": "ImageObject",
|
|
44
|
+
"contentUrl" : "https://www.m1key.me/photography/<%= slug %>/<%= slug %>_01.jpg",
|
|
45
|
+
"caption": "<%= title %>"
|
|
46
|
+
}
|
|
47
|
+
],
|
|
48
|
+
"description": "<%= title %> — Photography and Art portfolio by Michal Huniewicz"
|
|
49
|
+
}
|
|
50
|
+
</script>
|
|
51
|
+
</head>
|
|
52
|
+
|
|
53
|
+
<body>
|
|
54
|
+
|
|
55
|
+
<main class="bigger-container">
|
|
56
|
+
<nav class="navigation" aria-label="Gallery navigation">
|
|
57
|
+
<ul>
|
|
58
|
+
<li><a href="../" class="nav-link">< Back to Photography</a></li>
|
|
59
|
+
<li><a href="../../" class="nav-link"><< Back to M1key.me</a></li>
|
|
60
|
+
</ul>
|
|
61
|
+
</nav>
|
|
62
|
+
|
|
63
|
+
<header class="site-header">
|
|
64
|
+
<h1 class="page-title"><%= title %> by Michal Huniewicz</h1>
|
|
65
|
+
</header>
|
|
66
|
+
|
|
67
|
+
<section class="information" aria-labelledby="info-heading">
|
|
68
|
+
<h2 id="info-heading" class="visually-hidden">About this shoot</h2>
|
|
69
|
+
<%= description -%>
|
|
70
|
+
|
|
71
|
+
<p class="smallprint">
|
|
72
|
+
<span id="sources">Sources</span>:<% sources.each_with_index do |source, index| %>
|
|
73
|
+
<a href="<%= source %>" class="nav-link">source <%= index + 1 %></a><% if (index + 1) < sources.size %>,<% else %>.<% end -%>
|
|
74
|
+
<% end %>
|
|
75
|
+
</p>
|
|
76
|
+
<% if small_print.to_s != '' %>
|
|
77
|
+
<p><%= small_print -%></p><% end %>
|
|
78
|
+
<p class="smallprint">Uploaded on: <%= upload_date %>.</p>
|
|
79
|
+
</section>
|
|
80
|
+
|
|
81
|
+
<figure class="map">
|
|
82
|
+
<a href="<%= map_url %>"
|
|
83
|
+
target="_blank" rel="noopener noreferrer">
|
|
84
|
+
<img src="<%= slug %>_map.png" alt="Map showing <%= map_title %> location (opens in OpenStreetMap)"
|
|
85
|
+
title="<%= map_title %>" width="320" height="240" />
|
|
86
|
+
</a>
|
|
87
|
+
<figcaption class="visually-hidden">Map showing <%= map_title %> location (opens in OpenStreetMap)</figcaption>
|
|
88
|
+
</figure>
|
|
89
|
+
<% photos.each do |photo| %>
|
|
90
|
+
<article class="photo" id="<%= photo.id %>">
|
|
91
|
+
<header>
|
|
92
|
+
<h2 class="title"><a href="#<%= photo.id %>" title="Link to this photo"><%= photo.title %></a></h2>
|
|
93
|
+
</header>
|
|
94
|
+
<figure class="image-container">
|
|
95
|
+
<img src="<%= slug %>_<%= photo.id %>.jpg" alt="<%= photo.title %>" loading="lazy" />
|
|
96
|
+
<figcaption>
|
|
97
|
+
<span class="description"><%= photo.description %></span>
|
|
98
|
+
<span class="technical">ISO <%= photo.metadata.iso %>, <%= photo.metadata.focal_length %>mm, f/<%= photo.metadata.f_number %>, <%= photo.metadata.exposure_time %>s.<% if photo.technical_info.to_s != '' %> <%= photo.technical_info %><% end %></span>
|
|
99
|
+
</figcaption>
|
|
100
|
+
</figure>
|
|
101
|
+
</article>
|
|
102
|
+
<% end %>
|
|
103
|
+
<nav class="navigation" aria-label="Gallery navigation bottom">
|
|
104
|
+
<ul>
|
|
105
|
+
<li><a href="../" class="nav-link">< Back to Photography</a></li>
|
|
106
|
+
<li><a href="../../" class="nav-link"><< Back to M1key.me</a></li>
|
|
107
|
+
</ul>
|
|
108
|
+
</nav>
|
|
109
|
+
|
|
110
|
+
</main>
|
|
111
|
+
|
|
112
|
+
<footer class="site-footer">
|
|
113
|
+
<nav class="footer-nav" aria-label="Site navigation">
|
|
114
|
+
<ul>
|
|
115
|
+
<li><a href="https://www.m1key.me" class="nav-link">M1key.me</a></li>
|
|
116
|
+
<li><a href="https://www.m1key.me/photography/" class="nav-link">Photography</a></li>
|
|
117
|
+
<li><a href="https://www.m1key.me/about_me/" class="nav-link">About me</a></li>
|
|
118
|
+
<li><a href="https://www.flickr.com/photos/m1key-me/" rel="noopener noreferrer" class="nav-link">Flickr</a></li>
|
|
119
|
+
</ul>
|
|
120
|
+
</nav>
|
|
121
|
+
|
|
122
|
+
<div class="legal">
|
|
123
|
+
<p>©2010–<%= year %> Michal Huniewicz — m1key.me</p>
|
|
124
|
+
</div>
|
|
125
|
+
</footer>
|
|
126
|
+
|
|
127
|
+
</body>
|
|
128
|
+
|
|
129
|
+
</html>
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
require_relative 'string_utils'
|
|
2
|
+
|
|
3
|
+
module GalleryGenerator
|
|
4
|
+
class ViewableGallery
|
|
5
|
+
attr_reader :title, :description, :slug, :sources, :upload_date, :map_url, :map_title, :year, :photos, :small_print
|
|
6
|
+
def initialize(title, description, slug, sources, upload_date, map_url, map_title, year, viewable_photos, small_print)
|
|
7
|
+
@title = String.new(title)
|
|
8
|
+
@description = description
|
|
9
|
+
@slug = slug
|
|
10
|
+
@sources = sources
|
|
11
|
+
@upload_date = upload_date
|
|
12
|
+
@map_url = map_url
|
|
13
|
+
@map_title = map_title
|
|
14
|
+
@year = year
|
|
15
|
+
@photos = viewable_photos
|
|
16
|
+
@small_print = small_print
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def get_binding
|
|
20
|
+
binding
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def update_using(*update_functions)
|
|
24
|
+
updated_gallery = MutableViewableGallery.new(self)
|
|
25
|
+
|
|
26
|
+
update_functions.each do |update_function|
|
|
27
|
+
updated_gallery = update_function.call(updated_gallery)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
ViewableGallery.new(updated_gallery.title, updated_gallery.description, updated_gallery.slug,
|
|
31
|
+
updated_gallery.sources, updated_gallery.upload_date, updated_gallery.map_url,
|
|
32
|
+
updated_gallery.map_title, updated_gallery.year, to_viewable_photos(updated_gallery.photos), updated_gallery.small_print)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def to_viewable_photos(mutable_viewable_photos)
|
|
36
|
+
mutable_viewable_photos.map do |mutable_viewable_photo|
|
|
37
|
+
ViewablePhoto.new(mutable_viewable_photo.id, mutable_viewable_photo.title, mutable_viewable_photo.description, mutable_viewable_photo.metadata, mutable_viewable_photo.technical_info)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
class MutableViewableGallery
|
|
44
|
+
attr_accessor :title, :description, :slug, :sources, :upload_date, :map_url, :map_title, :year, :photos, :small_print
|
|
45
|
+
def initialize(viewable_gallery)
|
|
46
|
+
@title = viewable_gallery.title
|
|
47
|
+
@description = viewable_gallery.description
|
|
48
|
+
@slug = viewable_gallery.slug
|
|
49
|
+
@sources = viewable_gallery.sources
|
|
50
|
+
@upload_date = viewable_gallery.upload_date
|
|
51
|
+
@map_url = viewable_gallery.map_url
|
|
52
|
+
@map_title = viewable_gallery.map_title
|
|
53
|
+
@year = viewable_gallery.year
|
|
54
|
+
@photos = to_mutable_viewable_photos(viewable_gallery.photos)
|
|
55
|
+
@small_print = viewable_gallery.small_print
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def to_mutable_viewable_photos(viewable_photos)
|
|
59
|
+
viewable_photos.map do |viewable_photo|
|
|
60
|
+
MutableViewablePhoto.new(viewable_photo)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
module GalleryGenerator
|
|
2
|
+
class ViewablePhoto
|
|
3
|
+
attr_reader :id, :title, :description, :metadata, :technical_info
|
|
4
|
+
def initialize(id, title, description, metadata, technical_info)
|
|
5
|
+
@id = id
|
|
6
|
+
@title = title
|
|
7
|
+
@description = description
|
|
8
|
+
@metadata = metadata
|
|
9
|
+
@technical_info = technical_info
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
class MutableViewablePhoto
|
|
14
|
+
attr_accessor :id, :title, :description, :metadata, :technical_info
|
|
15
|
+
def initialize(viewable_photo)
|
|
16
|
+
@id = viewable_photo.id
|
|
17
|
+
@title = viewable_photo.title
|
|
18
|
+
@description = viewable_photo.description
|
|
19
|
+
@metadata = viewable_photo.metadata
|
|
20
|
+
@technical_info = viewable_photo.technical_info
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
module GalleryGenerator
|
|
2
|
+
class ViewablePhotoMetadata
|
|
3
|
+
attr_reader :original_file_name, :height, :iso, :focal_length, :f_number, :exposure_time
|
|
4
|
+
def initialize(original_file_name, height, iso, focal_length, f_number, exposure_time)
|
|
5
|
+
@original_file_name = original_file_name
|
|
6
|
+
@height = height
|
|
7
|
+
@iso = iso
|
|
8
|
+
@focal_length = focal_length
|
|
9
|
+
@f_number = f_number
|
|
10
|
+
@exposure_time = exposure_time
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
require_relative 'm1key_gallery_generator/version'
|
|
2
|
+
require_relative 'm1key_gallery_generator/viewable_gallery'
|
|
3
|
+
require_relative 'm1key_gallery_generator/viewable_photo'
|
|
4
|
+
require_relative 'm1key_gallery_generator/gallery_config'
|
|
5
|
+
require_relative 'm1key_gallery_generator/photos_utils'
|
|
6
|
+
require_relative 'm1key_gallery_generator/gallery_mutators'
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
lib = File.expand_path('lib', __dir__)
|
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
3
|
+
require 'm1key_gallery_generator/version'
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |spec|
|
|
6
|
+
spec.name = 'm1key_gallery_generator'
|
|
7
|
+
spec.version = M1keyGalleryGenerator::VERSION
|
|
8
|
+
spec.authors = ['Mike Huniewicz']
|
|
9
|
+
spec.email = ['REDACTED']
|
|
10
|
+
spec.required_ruby_version = '>= 3.0'
|
|
11
|
+
|
|
12
|
+
spec.metadata['allowed_push_host'] = 'https://rubygems.org'
|
|
13
|
+
|
|
14
|
+
spec.summary = 'm1key.me-style gallery generator.'
|
|
15
|
+
spec.description = 'This gem allows you to generate a m1key.me-style gallery based on the JPG files in the working directory.'
|
|
16
|
+
spec.homepage = 'https://github.com/m1key/m1key_gallery_generator'
|
|
17
|
+
spec.license = 'MIT'
|
|
18
|
+
|
|
19
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
|
20
|
+
spec.bindir = 'exe'
|
|
21
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
|
22
|
+
spec.require_paths = ['lib']
|
|
23
|
+
|
|
24
|
+
spec.add_runtime_dependency 'exifr', '~> 1.4', '>= 1.4.0'
|
|
25
|
+
|
|
26
|
+
spec.add_development_dependency 'bundler', '= 2.7.2'
|
|
27
|
+
spec.add_development_dependency 'rake', '~> 13.0'
|
|
28
|
+
spec.add_development_dependency 'minitest', '~> 5.15'
|
|
29
|
+
spec.add_development_dependency 'rspec', '~> 3.13'
|
|
30
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: m1key_gallery_generator
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.3.2
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Mike Huniewicz
|
|
8
|
+
bindir: exe
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: exifr
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - "~>"
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '1.4'
|
|
19
|
+
- - ">="
|
|
20
|
+
- !ruby/object:Gem::Version
|
|
21
|
+
version: 1.4.0
|
|
22
|
+
type: :runtime
|
|
23
|
+
prerelease: false
|
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
25
|
+
requirements:
|
|
26
|
+
- - "~>"
|
|
27
|
+
- !ruby/object:Gem::Version
|
|
28
|
+
version: '1.4'
|
|
29
|
+
- - ">="
|
|
30
|
+
- !ruby/object:Gem::Version
|
|
31
|
+
version: 1.4.0
|
|
32
|
+
- !ruby/object:Gem::Dependency
|
|
33
|
+
name: bundler
|
|
34
|
+
requirement: !ruby/object:Gem::Requirement
|
|
35
|
+
requirements:
|
|
36
|
+
- - '='
|
|
37
|
+
- !ruby/object:Gem::Version
|
|
38
|
+
version: 2.7.2
|
|
39
|
+
type: :development
|
|
40
|
+
prerelease: false
|
|
41
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
42
|
+
requirements:
|
|
43
|
+
- - '='
|
|
44
|
+
- !ruby/object:Gem::Version
|
|
45
|
+
version: 2.7.2
|
|
46
|
+
- !ruby/object:Gem::Dependency
|
|
47
|
+
name: rake
|
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
|
49
|
+
requirements:
|
|
50
|
+
- - "~>"
|
|
51
|
+
- !ruby/object:Gem::Version
|
|
52
|
+
version: '13.0'
|
|
53
|
+
type: :development
|
|
54
|
+
prerelease: false
|
|
55
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
56
|
+
requirements:
|
|
57
|
+
- - "~>"
|
|
58
|
+
- !ruby/object:Gem::Version
|
|
59
|
+
version: '13.0'
|
|
60
|
+
- !ruby/object:Gem::Dependency
|
|
61
|
+
name: minitest
|
|
62
|
+
requirement: !ruby/object:Gem::Requirement
|
|
63
|
+
requirements:
|
|
64
|
+
- - "~>"
|
|
65
|
+
- !ruby/object:Gem::Version
|
|
66
|
+
version: '5.15'
|
|
67
|
+
type: :development
|
|
68
|
+
prerelease: false
|
|
69
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
70
|
+
requirements:
|
|
71
|
+
- - "~>"
|
|
72
|
+
- !ruby/object:Gem::Version
|
|
73
|
+
version: '5.15'
|
|
74
|
+
- !ruby/object:Gem::Dependency
|
|
75
|
+
name: rspec
|
|
76
|
+
requirement: !ruby/object:Gem::Requirement
|
|
77
|
+
requirements:
|
|
78
|
+
- - "~>"
|
|
79
|
+
- !ruby/object:Gem::Version
|
|
80
|
+
version: '3.13'
|
|
81
|
+
type: :development
|
|
82
|
+
prerelease: false
|
|
83
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
84
|
+
requirements:
|
|
85
|
+
- - "~>"
|
|
86
|
+
- !ruby/object:Gem::Version
|
|
87
|
+
version: '3.13'
|
|
88
|
+
description: This gem allows you to generate a m1key.me-style gallery based on the
|
|
89
|
+
JPG files in the working directory.
|
|
90
|
+
email:
|
|
91
|
+
- REDACTED
|
|
92
|
+
executables: []
|
|
93
|
+
extensions: []
|
|
94
|
+
extra_rdoc_files: []
|
|
95
|
+
files:
|
|
96
|
+
- ".gitignore"
|
|
97
|
+
- ".rspec"
|
|
98
|
+
- Gemfile
|
|
99
|
+
- LICENSE.txt
|
|
100
|
+
- README.md
|
|
101
|
+
- Rakefile
|
|
102
|
+
- bin/console
|
|
103
|
+
- bin/setup
|
|
104
|
+
- lib/m1key_gallery_generator.rb
|
|
105
|
+
- lib/m1key_gallery_generator/console_utils.rb
|
|
106
|
+
- lib/m1key_gallery_generator/gallery_config.rb
|
|
107
|
+
- lib/m1key_gallery_generator/gallery_mutators.rb
|
|
108
|
+
- lib/m1key_gallery_generator/photos_utils.rb
|
|
109
|
+
- lib/m1key_gallery_generator/string_utils.rb
|
|
110
|
+
- lib/m1key_gallery_generator/template.erb
|
|
111
|
+
- lib/m1key_gallery_generator/version.rb
|
|
112
|
+
- lib/m1key_gallery_generator/viewable_gallery.rb
|
|
113
|
+
- lib/m1key_gallery_generator/viewable_photo.rb
|
|
114
|
+
- lib/m1key_gallery_generator/viewable_photo_metadata.rb
|
|
115
|
+
- m1key_gallery_generator.gemspec
|
|
116
|
+
homepage: https://github.com/m1key/m1key_gallery_generator
|
|
117
|
+
licenses:
|
|
118
|
+
- MIT
|
|
119
|
+
metadata:
|
|
120
|
+
allowed_push_host: https://rubygems.org
|
|
121
|
+
rdoc_options: []
|
|
122
|
+
require_paths:
|
|
123
|
+
- lib
|
|
124
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
125
|
+
requirements:
|
|
126
|
+
- - ">="
|
|
127
|
+
- !ruby/object:Gem::Version
|
|
128
|
+
version: '3.0'
|
|
129
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
130
|
+
requirements:
|
|
131
|
+
- - ">="
|
|
132
|
+
- !ruby/object:Gem::Version
|
|
133
|
+
version: '0'
|
|
134
|
+
requirements: []
|
|
135
|
+
rubygems_version: 3.7.2
|
|
136
|
+
specification_version: 4
|
|
137
|
+
summary: m1key.me-style gallery generator.
|
|
138
|
+
test_files: []
|