piccle 0.1.0.rc1 → 0.1.1.pre

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: db3f8798e1dfc0e62bf41b3cdda06bd2f9b55f2331cdf43355734d64b62b2a71
4
- data.tar.gz: af3016c01de9d25f078369ea6c0c83865bae176c08230009097fca586792505e
3
+ metadata.gz: 81cf1f58e254874d67a158e428edc3c308557bdcaa1f890c845c1a579faac74d
4
+ data.tar.gz: '04148646454d3b3b5b882fa56c8a0f563dd8e6fc311316e20f63e3170998264d'
5
5
  SHA512:
6
- metadata.gz: ea4bac152db471cf72aaaab4df57779d6fb756657581e4cc86fa425e29d0627793c0d6dde08c4f88a9b593afeed75e65a28792f034ac4846003d9ba4eda8a5f9
7
- data.tar.gz: 4cbc517e96ab3b3a49cd7100ad515f3334a9a097bccb7ae10f559e8bc8fdd8b2b8b7a49bdf799167876381c76bac7a2edb2046ba9a29fec6e23fccead6e03ca4
6
+ metadata.gz: 7ea526664d305b466eaa894817ebc5066391e6d24c51b4287227a7b12a2b7189c96875e572bd900df38ac6d08a26f9898996e0000daba5e92016dbca0bd489fb
7
+ data.tar.gz: 90d41a1d0d0517b56fe945d672213edf662b59daa5ea864cfebb07e8d0604bc3b13c068ed08571ed7f86be1ec22700d9e3960bf5f57b106bd2dff513e3db30dd
File without changes
data/NOTES.md CHANGED
@@ -2,44 +2,38 @@
2
2
 
3
3
  ## Roadmap
4
4
 
5
- v0.1
6
- - Test it works on other computers
7
- - Better iPad display
8
- - Update readme
9
-
10
5
  v0.2
11
- - Paginated indexes
12
6
  - Render subnav (ie. if you're in 2020 view, it'll show the months)
13
- - People browser
14
7
  - Show most recently added photos
15
8
 
16
9
  v0.3
17
10
  - Track hash changes, generate redirects and htaccess file
18
- - Collapsible events on the front page (maybe)
19
11
  - Better display of streams on photo show page
20
12
 
21
13
  v0.4
22
14
  - Client-side rendering
23
15
 
16
+ v0.5
17
+ - Section browse pages
18
+
24
19
 
25
20
  ## Bugs
26
21
  - Don't fail horribly if there are no images.
27
22
  - More useful error output if the given images directory does not exist.
23
+ - Figure out timezones around collapsed events
28
24
 
29
25
 
30
26
  ## Improvements
31
27
 
28
+ - Add "section browse" pages?
29
+ - Collapse "collapsed" sections everywhere, EXCEPT that particular stream.
32
30
  - Add ordering to all the various substreams. Almost done, apart from sorting days and months in the datestream.
33
- - Update the photo update method so it also REMOVES keywords from files.
34
- - Store changed MD5 hashes, generate redirect pages for those.
31
+ - Store changed MD5 hashes, generate redirect pages for those.
35
32
  - Add a cleanup function that removes old images/HTML.
36
- - Make keywords case insensitive.
37
- - Write a readme.
38
- - Add "collapsed" view for events so they show up in one tile.
39
- - Add links to event tiles that link to the event page.
40
33
  - Can we detect fixed focal length cameras in the metadata?
41
34
  - Maybe combine substream path with include prefix? So we don't have to do two {{foo}}{{bar}} on every link.
42
35
  - Put current stream first on photo page
36
+ - Generally improve the stream display on photo page. Filter out samey streams, maybe only show a maximum of 4?
43
37
  - Update nav to render subnav too, for current section.
44
38
  - Finish commenting the BaseStream.
45
39
 
@@ -52,14 +46,11 @@ v0.4
52
46
  ----- All the notes below are kind of outdated ------
53
47
 
54
48
  - JS slideshow at the top of the index page??
55
- - Convert database.rake tasks over to the Sequel way of doing things
56
49
  - Our current find-or-create for photos relates to the file name/path. It should probably use that, and/or the MD5.
57
50
  - Focal length is currently available as a fraction in the EXIF, but we're storing it as a float. Maybe we'd like to store it as a fraction instead?
58
51
 
59
52
  # Development notes
60
53
 
61
- - The EXIF tag calls the camera model "model", but in the DB schema we called it "camera_name". We might want to unify this.
62
- - NB. Sequel will throw a wobbly if we use "model" as a method name.
63
54
  - Add a meaningful "alt" tag in the photo thumbnail.
64
55
 
65
56
  - Streams should maybe be able to generate "top-level" pages, so we could have paths like "example.com/2008" rather than "example.com/by-date/2008".
data/README.md CHANGED
@@ -1,3 +1,5 @@
1
+ ![Piccle logo](https://github.com/creature/piccle/blob/master/assets/icons/android-chrome-192x192.png?raw=true)
2
+
1
3
  # Piccle
2
4
 
3
5
  Piccle is a static photo gallery generator. Purposefully designed with no admin interface, it builds the gallery from
@@ -17,10 +19,24 @@ though these are unlikely to be added unless you set them up first.
17
19
 
18
20
  ## Getting started
19
21
 
22
+ ### Quickstart
23
+
24
+ ```bash
25
+ gem install piccle --pre
26
+ piccle generate -i path/to/your/image/dir # Produces a site in generated/
27
+ piccle help # To see more options
28
+ ```
29
+
30
+ ### Installing Piccle
31
+
20
32
  Piccle requires a modern(ish) version of Ruby. If you don't have one already, then I like [rbenv](https://github.com/rbenv/rbenv#readme)
21
33
  as a Ruby version manager.
22
34
 
23
- 1. Run `gem install piccle` to install the software.
35
+ The default renderer relies on [NodeJS](https://nodejs.org/en/) being available in your `$PATH`. If this isn't possible
36
+ for you, you can run Piccle with `--ruby-renderer` – but this is typically 10 times slower.
37
+
38
+
39
+ 1. Run `gem install piccle --pre` to install the software.
24
40
  1. Piccle can run from anywhere, but things will be easier if you created a dedicated directory for it:
25
41
  ```bash
26
42
  mkdir -p piccle/images
@@ -37,6 +53,8 @@ as a Ruby version manager.
37
53
  open `generated/index.html` in your browser.
38
54
  1. You're done!
39
55
 
56
+ ## Available Options
57
+
40
58
  Piccle has two subcommands, `generate` and `geocode`. `generate` is the default task and is the same as running
41
59
  without specifying a subcommand. `geocode` uses the Data Science Toolkit to look up locations in the database.
42
60
  `piccle help generate` will display the various options:
@@ -54,6 +72,8 @@ without specifying a subcommand. `geocode` uses the Data Science Toolkit to look
54
72
  (so people can subscribe to updates) and OpenGraph tags (which give nice embeds on social media) require a full URL.
55
73
  They won't be generated if this is not set.
56
74
  * `--debug` turns on debug mode, which adds some extra logging.
75
+ * `--ruby-renderer` uses a rendering engine that does not require your system to have NodeJS installed (instead, it
76
+ uses a [wrapper for libv8](https://github.com/rubyjs/therubyracer)). The output is the same, but it's much slower.
57
77
 
58
78
 
59
79
  ## Metadata Used
@@ -64,6 +84,33 @@ without specifying a subcommand. `geocode` uses the Data Science Toolkit to look
64
84
  * **Keywords** are shown, and exposed as "topics".
65
85
  * **Location** is shown. If your photos have a city/state/country specified in their data, then that's used; otherwise,
66
86
  Piccle will attempt to geocode them based on embedded latitude/longitude.
87
+ * **People** are browsable, based on the "Person Shown" IPTC field.
88
+
89
+
90
+ ## Events
91
+
92
+ Piccle allows you to create named events in a separate [YAML file](https://learnxinyminutes.com/docs/yaml/). Each event
93
+ gets a dedicated index page, and a callout on the main index. It's useful for highlighting pictures from a particular
94
+ photoshoot, or drawing attention to a trip.
95
+
96
+ Events must have a name, and must either be `at` a particular date or within a particular date span (`from` and `to`).
97
+ Events may also be marked as `collapsed`; collapsed events don't show all their photos on an index page. Instead, they
98
+ show a quilt tile with some of the photos. It's a good way to avoid flooding your front page with a lot of
99
+ similar-looking photos.
100
+
101
+ This `events.yaml` file defines two events: a photoshoot I did themed around googly eyes, and a trip to the UK in 2019.
102
+ (The example shows dates only, but you can include times too.)
103
+
104
+ ```yaml
105
+ -
106
+ name: Googly Eyes
107
+ at: 2019-03-23
108
+ collapsed: true
109
+ -
110
+ name: UK 2019
111
+ from: 2019-01-24
112
+ to: 2019-02-09
113
+ ```
67
114
 
68
115
  -------
69
116
 
@@ -71,8 +118,9 @@ without specifying a subcommand. `geocode` uses the Data Science Toolkit to look
71
118
 
72
119
  Geolocation is provided by the free [Data Science Toolkit API](http://www.datasciencetoolkit.org/developerdocs#coordinates2politics).
73
120
  The test images are public domain images by [Sasin Tipchai](https://pixabay.com/photos/elephant-animals-asia-large-1822636/),
74
- [Timo Schlüter](https://pixabay.com/photos/kingfisher-bird-blue-plumage-1905255/), and
75
- [Jill Wellington](https://pixabay.com/photos/spring-bird-bird-spring-blue-2295431/).
121
+ [Timo Schlüter](https://pixabay.com/photos/kingfisher-bird-blue-plumage-1905255/),
122
+ [Jill Wellington](https://pixabay.com/photos/spring-bird-bird-spring-blue-2295431/), and
123
+ [Pascal Vannevel](https://pixabay.com/photos/usa-canyonlands-mesa-arch-natuur-5009894/).
76
124
 
77
125
 
78
126
  ## License
@@ -96,37 +144,37 @@ tool for managing metadata yet, but here are some options:
96
144
  ### Adobe Bridge
97
145
 
98
146
  Pros
99
- : Available for free, without a paid Creative Cloud subscription
100
- : Lets you build a library of keywords, with a nested heirarchy, so it's easier to use a set of standard tags with your photos
101
- : Can [edit titles, descriptions, and locations](https://helpx.adobe.com/ca/bridge/using/metadata-adobe-bridge.html)
102
- : Good filtering support: it's easy to find pictures lacking metadata
147
+ - Available for free, without a paid Creative Cloud subscription
148
+ - Lets you build a library of keywords, with a nested heirarchy, so it's easier to use a set of standard tags with your photos
149
+ - Can [edit titles, descriptions, and locations](https://helpx.adobe.com/ca/bridge/using/metadata-adobe-bridge.html)
150
+ - Good filtering support: it's easy to find pictures lacking metadata
103
151
 
104
152
  Cons
105
- : You still need a Creative Cloud account to download it
106
- : You must install the Adobe Creative Cloud stuff to get Bridge
107
- : Can't place photos on a map for adding latitude/longitude.
153
+ - You still need a Creative Cloud account to download it
154
+ - You must install the Adobe Creative Cloud stuff to get Bridge
155
+ - Can't place photos on a map for adding latitude/longitude.
108
156
 
109
157
 
110
158
  ### macOS Preview
111
159
 
112
160
  Pros
113
- : If you use macOS you already have it
114
- : Access the inspector (⌘-I or File → Inspect) and choose the "Keywords" panel to add/remove keywords
161
+ - If you use macOS you already have it
162
+ - Access the inspector (⌘-I or File → Inspect) and choose the "Keywords" panel to add/remove keywords
115
163
 
116
164
  Cons
117
- : Can't edit title, description, or location
118
- : Can't build a library of tags
165
+ - Can't edit title, description, or location
166
+ - Can't build a library of tags
119
167
 
120
168
 
121
169
  ### Affinity Photo
122
170
 
123
171
  Pros
124
- : Lets you edit titles, descriptions, locations, keywords, and add latitude/longitude via a map
125
- : Is generally a delightful image editor
172
+ - Lets you edit titles, descriptions, locations, keywords, and add latitude/longitude via a map
173
+ - Is generally a delightful image editor
126
174
 
127
175
  Cons
128
- : Longwinded for bulk edits: open photo, switch to Develop mode, change to metadata tab, switch between "File" and "IPTC (Image)" sections
129
- : No tag library - you must type in a comma-separated string
176
+ - Longwinded for bulk edits: open photo, switch to Develop mode, change to metadata tab, switch between "File" and "IPTC (Image)" sections
177
+ - No tag library - you must type in a comma-separated string
130
178
 
131
179
 
132
180
  ## Automation
@@ -10,22 +10,6 @@ a {
10
10
  color: #7b6240;
11
11
  }
12
12
 
13
- ul li a {
14
- transition: opacity ease-in 0.22s;
15
- color: #444444;
16
- text-decoration: none;
17
- }
18
-
19
- ul:hover a {
20
- opacity: 0.5;
21
- }
22
-
23
- ul:hover a:hover {
24
- opacity: 1;
25
- text-decoration: underline;
26
- }
27
-
28
-
29
13
  header, footer {
30
14
  max-width: 960px;
31
15
  margin-right: auto;
@@ -110,12 +94,27 @@ nav li a {
110
94
  display: block;
111
95
  }
112
96
 
97
+ nav ul li a {
98
+ transition: opacity ease-in 0.22s;
99
+ color: #444444;
100
+ text-decoration: none;
101
+ }
102
+
103
+ nav ul:hover a {
104
+ opacity: 0.5;
105
+ }
106
+
107
+ nav ul:hover a:hover {
108
+ opacity: 1;
109
+ text-decoration: underline;
110
+ }
111
+
113
112
  section h2 {
114
113
  font-size: 1em;
115
114
  }
116
115
 
117
116
  /* This is both a flexbox item (of the main element) and a Flexbox container itself (for the photos). */
118
- .photos {
117
+ #photos {
119
118
  flex: auto;
120
119
  display: grid;
121
120
  grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
@@ -123,7 +122,7 @@ section h2 {
123
122
  align-content: start;
124
123
  }
125
124
 
126
- .photos a img {
125
+ #photos a img {
127
126
  width: 100%;
128
127
  height: auto;
129
128
  }
@@ -143,9 +142,16 @@ section h2 {
143
142
  overflow: hidden;
144
143
  }
145
144
 
145
+ .event_block:hover a {
146
+ color: #fff;
147
+ text-decoration: underline;
148
+ }
149
+
150
+
146
151
  .event_block a {
147
152
  color: #ddd;
148
153
  text-decoration: none;
154
+ transition: all 0.22s;
149
155
  }
150
156
 
151
157
  .event_block.event_start {
@@ -166,6 +172,33 @@ section h2 {
166
172
  padding-left: 1rem;
167
173
  }
168
174
 
175
+ .event_block.collapsed {
176
+ background-size: cover;
177
+ }
178
+
179
+ .event_block.collapsed a {
180
+ -webkit-backdrop-filter: blur(5px) contrast(40%);
181
+ backdrop-filter: blur(5px);
182
+ border-radius: 5px;
183
+ padding: 1rem;
184
+ }
185
+
186
+ .pagination {
187
+ display: flex;
188
+ margin: 2rem;
189
+ justify-content: center;
190
+ }
191
+
192
+ .pagination ol {
193
+ list-style-type: none;
194
+ margin: 0 1rem;
195
+ padding: 0;
196
+ }
197
+
198
+ .pagination ol li {
199
+ display: inline-block;
200
+ }
201
+
169
202
  .keywords {
170
203
  margin: 72px 72px 18px;
171
204
  line-height: 2;
@@ -300,6 +333,7 @@ ul.location li:last-of-type:after {
300
333
  object-fit: contain;
301
334
  min-width: 0;
302
335
  max-width: 100%;
336
+ max-height: 100vh;
303
337
  }
304
338
 
305
339
  /* --------- Mobile styles ---------- */
@@ -363,10 +397,18 @@ ul.location li:last-of-type:after {
363
397
  width: 50%;
364
398
  }
365
399
 
366
- .photos {
400
+ #photos {
367
401
  grid-template-columns: repeat(3, 1fr);
368
402
  }
369
403
 
404
+ .pagination {
405
+ justify-content: space-evenly;
406
+ }
407
+
408
+ .pagination ol { /* No page numbers on tiny displays, just next/previous */
409
+ display: none;
410
+ }
411
+
370
412
  .event_block {
371
413
  font-size: 18px;
372
414
  }
@@ -393,5 +435,31 @@ ul.location li:last-of-type:after {
393
435
  margin-left: 1rem;
394
436
  margin-right: 1rem;
395
437
  }
438
+ }
439
+
440
+ /* Smaller margins on smallish displays (like iPads) */
441
+ @media(min-width: 689px) and (max-width: 1024px) {
442
+ body {
443
+ margin: 0 30px;
444
+ }
445
+
446
+ nav {
447
+ margin-right: 15px;
448
+ max-width: 150px;
449
+ }
450
+ }
451
+
452
+ /* More room for pages on smallish screens (ie. phones or tablets, probably) */
453
+ @media(max-width: 1024px) {
454
+ .pagination {
455
+ justify-content: space-between;
456
+ }
457
+
458
+ .pagination ol li {
459
+ margin-right: 1rem;
460
+ }
396
461
 
462
+ .pagination ol li:last-child {
463
+ margin-right: 0;
464
+ }
397
465
  }
data/bin/piccle CHANGED
@@ -163,6 +163,9 @@ class CLI < Thor
163
163
  start_time = Time.now
164
164
  puts "Generating website..."
165
165
 
166
+ FileUtils.mkdir_p(Piccle.config.output_dir)
167
+ generate_templates
168
+
166
169
  parser = new_parser_with_streams
167
170
  parse_photos(parser)
168
171
  renderer = if Piccle.config.ruby_renderer?
@@ -171,8 +174,6 @@ class CLI < Thor
171
174
  Piccle::JsRenderer.new(parser)
172
175
  end
173
176
 
174
- FileUtils.mkdir_p(Piccle.config.output_dir)
175
- generate_templates(renderer)
176
177
  generate_atom_feeds(parser, renderer)
177
178
  generate_html_indexes(parser, renderer)
178
179
  generate_html_photos(parser, renderer)
@@ -190,6 +191,7 @@ class CLI < Thor
190
191
  p.add_stream(Piccle::Streams::LocationStream)
191
192
  p.add_stream(Piccle::Streams::EventStream)
192
193
  p.add_stream(Piccle::Streams::CameraStream)
194
+ p.add_stream(Piccle::Streams::PersonStream)
193
195
  p.add_stream(Piccle::Streams::KeywordStream)
194
196
  end
195
197
  end
@@ -206,16 +208,24 @@ class CLI < Thor
206
208
  # Given a parser object, generate some HTML index pages from the data it contains.
207
209
  def generate_html_indexes(parser, renderer)
208
210
  puts " ... generating HTML indexes ..."
209
- print " ... generating main index ... "
210
- File.write(File.join(Piccle.config.output_dir, "index.html"), renderer.render_main_index)
211
+ print " ... generating main index "
212
+ paginated_main_index = renderer.render_main_index
213
+ print "(#{paginated_main_index.count} page(s)) ... "
214
+ paginated_main_index.each_with_index do |page, index|
215
+ File.write(File.join(Piccle.config.output_dir, "#{renderer.index_page_name_for(index)}.html"), page)
216
+ end
211
217
  puts "Done."
212
218
 
213
219
  parser.subsections.each do |subsection|
214
220
  if parser.subsection_photo_hashes(subsection).any?
215
221
  subdir = File.join(Piccle.config.output_dir, *subsection)
216
- print " ... generating #{subdir} index ... "
222
+ print " ... generating #{subdir} index "
217
223
  FileUtils.mkdir_p(subdir)
218
- File.write(File.join(subdir, "index.html"), renderer.render_index(subsection))
224
+ paginated_subsection_index = renderer.render_index(subsection)
225
+ print "(#{paginated_subsection_index.count} page(s)) ... "
226
+ paginated_subsection_index.each_with_index do |page, index|
227
+ File.write(File.join(subdir, "#{renderer.index_page_name_for(index)}.html"), page)
228
+ end
219
229
  puts "Done."
220
230
  end
221
231
  end
@@ -295,6 +305,10 @@ class CLI < Thor
295
305
  # Generates "quilts" - stitched together images for each section, which we use in OpenGraph tags.
296
306
  def generate_quilts(parser)
297
307
  puts "Generating gallery quilts (preview images for sharing galleries on social media)..."
308
+ unless Piccle.config.open_graph?
309
+ puts " Not generating most quilts, because no home URL is set."
310
+ end
311
+
298
312
  if Piccle.config.open_graph?
299
313
  thumbnail_path_proc = Proc.new { |k, v| File.join(Piccle.config.output_dir, "images", "thumbnails", "#{v[:hash]}.#{v[:file_name]}") }
300
314
 
@@ -303,23 +317,22 @@ class CLI < Thor
303
317
  main_quilt = Piccle::QuiltGenerator.generate_for(main_thumbnails)
304
318
  main_quilt.write(File.join(Piccle.config.output_dir, "quilt.jpg"))
305
319
  puts " Done."
320
+ end
306
321
 
307
- parser.subsections.each do |subsection|
308
- thumbnails = parser.subsection_photos(subsection).map(&thumbnail_path_proc)
309
- if thumbnails.any?
310
- output_path = File.join(Piccle.config.output_dir, *subsection, "quilt.jpg")
311
- print " ... Creating gallery quilt #{output_path}..."
312
- quilt = Piccle::QuiltGenerator.generate_for(thumbnails.first(9))
313
- quilt.write(output_path)
314
- puts " Done."
315
- end
322
+ parser.subsections.each do |subsection|
323
+ thumbnails = parser.subsection_photos(subsection).map(&thumbnail_path_proc)
324
+ # All sections get quilts if we're rendering OpenGraph tags; otherwise just collapsed sections.
325
+ if thumbnails.any? && (Piccle.config.open_graph? || parser.subsection_collapsed?(subsection))
326
+ output_path = File.join(Piccle.config.output_dir, *subsection, "quilt.jpg")
327
+ print " ... Creating gallery quilt #{output_path}..."
328
+ quilt = Piccle::QuiltGenerator.generate_for(thumbnails.first(9))
329
+ quilt.write(output_path)
330
+ puts " Done."
316
331
  end
317
- else
318
- puts " Not generating gallery quilt images, because no home URL is set."
319
332
  end
320
333
  end
321
334
 
322
- def generate_templates(_renderer)
335
+ def generate_templates
323
336
  puts " ... generating templates..."
324
337
  FileUtils.mkdir_p(File.join(Piccle.config.output_dir, "js"))
325
338
  File.write(File.join(Piccle.config.output_dir, "js", "index.handlebars"), Piccle::TemplateHelpers.compile_template("index"))