jekyll-flickr-utils 0.1.0

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 5ecf9cfe481ab49cbdbe3cdda762544467f75be8b88f548fa4504953e804067e
4
+ data.tar.gz: 258a913fb9833ad2d12608860f6b3a9d0e4a542ca4c3f04b198223aae824a9c6
5
+ SHA512:
6
+ metadata.gz: d23949729a14f7375428dc8d9731f0adee3f8b88e98743abaf6eddd8d488af3ddf8309773163bfcfd9484172cf282eb1a45032e0d086ef6f41496f53500a1d6d
7
+ data.tar.gz: 39eb671a361ab019ffe80b50f50caeff39b58f41ec2759c5f31280406e4f304abcfb8665d51a58acd22d853cef0c5e11ae92a5832a456f0d631bb67c82d90cd7
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2014-present Alfred Xing and the jekyll-archives contributors
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 all
13
+ 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 THE
21
+ SOFTWARE.
@@ -0,0 +1,172 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ require 'down'
5
+ require 'fileutils'
6
+ require 'flickr'
7
+ require 'jekyll'
8
+ require 'open-uri'
9
+ require 'sorbet-runtime'
10
+ require 'yaml'
11
+
12
+ module FlickrDownloader
13
+ class Generator < Jekyll::Generator
14
+ extend T::Sig
15
+
16
+ LOG_TOPIC = 'Flickr Downloader:'
17
+ JEKYLL_ROOT = T.let(Jekyll.configuration.source({}), String)
18
+ MANIFEST_FILE_PATH = T.let("#{JEKYLL_ROOT}/_data/flickr_photosets.yaml", String)
19
+
20
+ ConfigType = T.type_alias { T::Hash[String, T.untyped] }
21
+ ManifestType = T.type_alias { T::Hash[String, T::Hash[String, String]] }
22
+
23
+ DEFAULTS = T.let(
24
+ {
25
+ 'enabled' => false,
26
+ 'image_root' => 'assets/images/flickr_photosets',
27
+ 'force_refresh' => false,
28
+ 'image_size' => 'url_k'
29
+ }.freeze,
30
+ ConfigType
31
+ )
32
+
33
+ sig { params(config: ConfigType).void }
34
+ def initialize(config = {})
35
+ super
36
+
37
+ @config = T.let(DEFAULTS.merge(config.fetch('flickr_downloader', {})), ConfigType)
38
+
39
+ @flickr = T.let(nil, T.nilable(Flickr))
40
+
41
+ return unless enabled?
42
+
43
+ @flickr = Flickr.new(@config['api_key'], @config['shared_secret'])
44
+ @flickr.access_token = @config['access_token']
45
+ @flickr.access_secret = @config['access_secret']
46
+ end
47
+
48
+ sig { returns(T::Boolean) }
49
+ def enabled?
50
+ @config['enabled']
51
+ end
52
+
53
+ sig { params(site: Jekyll::Site).void }
54
+ def generate(site)
55
+ unless enabled?
56
+ Jekyll.logger.warn(LOG_TOPIC, 'Plugin disabled via config!')
57
+ return
58
+ end
59
+
60
+ posts = site.posts.docs.select do |post|
61
+ !post.data.fetch('disable_flickr_download', false) && post.data.fetch('flickr_photoset_id', nil)
62
+ end
63
+
64
+ FileUtils.touch(MANIFEST_FILE_PATH) if posts && !File.exist?(MANIFEST_FILE_PATH)
65
+
66
+ posts.each do |post|
67
+ flickr_photoset_id = post.data['flickr_photoset_id']
68
+
69
+ if flickr_photoset_id.is_a?(Array)
70
+ flickr_photoset_id.each { |photoset_id| download_photoset(photoset_id.to_s, post) }
71
+ elsif flickr_photoset_id
72
+ download_photoset(flickr_photoset_id.to_s, post)
73
+ end
74
+ end
75
+ end
76
+
77
+ sig { params(flickr_photoset_id: String).returns(T::Boolean) }
78
+ private def should_download?(flickr_photoset_id)
79
+ data = YAML.load_file(MANIFEST_FILE_PATH) || {}
80
+ # Ensure we treat the photoset ID as a String instead of an integer
81
+ data = data.transform_keys(&:to_s)
82
+ data.fetch(flickr_photoset_id, nil).nil? || @config['force_refresh']
83
+ end
84
+
85
+ sig { params(url: String, destination: String).void }
86
+ private def download_photo(url, destination)
87
+ Down.download(url, destination: "#{JEKYLL_ROOT}/#{destination}")
88
+ end
89
+
90
+ sig { params(manifest: ManifestType).void }
91
+ private def write_manifest_file(manifest)
92
+ data = YAML.load_file(MANIFEST_FILE_PATH) || {}
93
+ data = data.merge(manifest)
94
+ File.write(MANIFEST_FILE_PATH, YAML.dump(data))
95
+ end
96
+
97
+ sig { params(photoset_id: String, directory: String).returns(ManifestType) }
98
+ private def download_photoset_photos(photoset_id, directory)
99
+ directory = directory.chomp('/')
100
+
101
+ Jekyll.logger.info(LOG_TOPIC, "Downloading photos from Flickr photoset #{photoset_id} to #{directory}...")
102
+
103
+ image_size = @config['image_size']
104
+ # NOTE: Sorbet cannot find the dynamically-generated methods.
105
+ photoset = T.let(@flickr, T.untyped).photosets.getPhotos(
106
+ photoset_id: photoset_id,
107
+ extras: "description,url_o,#{image_size}"
108
+ )
109
+
110
+ manifest_images = {}
111
+ primary_photo_id = photoset.primary
112
+ primary_photo_filename = T.let(nil, T.nilable(String))
113
+
114
+ photoset.photo.each_with_index do |photo, index|
115
+ # NOTE: If the image is too small, Flickr may not have a URL matching the requested size.
116
+ # Fall back to the original size in such cases.
117
+ url = photo[image_size] || photo['url_o']
118
+ filename = format("#{directory}/%03d.jpg", (index + 1))
119
+
120
+ primary_photo_filename = filename if photo.id == primary_photo_id
121
+
122
+ manifest_images[filename] = {
123
+ 'metadata' => {
124
+ 'title' => photo.title,
125
+ 'description' => photo.description
126
+ }
127
+ }
128
+
129
+ download_photo(url, filename)
130
+ end
131
+
132
+ manifest = {
133
+ photoset_id => {
134
+ 'title' => photoset.title,
135
+ 'primary_image' => primary_photo_filename,
136
+ 'images' => manifest_images
137
+ }
138
+ }
139
+
140
+ Jekyll
141
+ .logger
142
+ .info(
143
+ LOG_TOPIC,
144
+ "Finished downloading #{manifest_images.length} photos from Flickr photoset #{photoset_id} to #{directory}."
145
+ )
146
+
147
+ manifest
148
+ end
149
+
150
+ sig { params(flickr_photoset_id: String, post: T.untyped).void }
151
+ private def download_photoset(flickr_photoset_id, post)
152
+ unless should_download?(flickr_photoset_id)
153
+ post_name = T.must(%r{_posts/(?<name>.+)\.md}.match(post.relative_path))['name']
154
+ Jekyll
155
+ .logger
156
+ .warn(
157
+ LOG_TOPIC,
158
+ # rubocop: disable Layout/LineLength
159
+ "Photos already downloaded for Flickr photoset #{flickr_photoset_id}, referenced by #{post_name}. Moving on."
160
+ # rubocop: enable Layout/LineLength
161
+ )
162
+ return
163
+ end
164
+
165
+ post_image_root = "#{@config['image_root']}/#{flickr_photoset_id}"
166
+ FileUtils.mkdir_p(post_image_root)
167
+
168
+ manifest = download_photoset_photos(flickr_photoset_id, post_image_root)
169
+ write_manifest_file(manifest)
170
+ end
171
+ end
172
+ end
@@ -0,0 +1,5 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ require 'flickr_downloader'
5
+ require 'post_image'
data/lib/post_image.rb ADDED
@@ -0,0 +1,92 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ require 'sorbet-runtime'
5
+
6
+ module Jekyll
7
+ module FlickrHelpers
8
+ extend T::Sig
9
+
10
+ PhotosetId = T.type_alias { Integer }
11
+
12
+ sig { params(photoset_id: T.any(PhotosetId, T::Array[PhotosetId])).returns(PhotosetId) }
13
+ def self.get_first_photoset_id(photoset_id)
14
+ photoset_id = T.must(photoset_id.first) if photoset_id.is_a?(Array)
15
+
16
+ photoset_id
17
+ end
18
+
19
+ sig { params(photoset_id: PhotosetId).returns(T.nilable(String)) }
20
+ def self.get_photoset_primary_photo(photoset_id)
21
+ path = Jekyll.sites.first.data['flickr_photosets'].fetch(photoset_id, {}).fetch('primary_image', nil)
22
+ path.nil? ? nil : "/#{path}"
23
+ rescue KeyError
24
+ nil
25
+ end
26
+ end
27
+
28
+ module PostImage
29
+ extend T::Sig
30
+
31
+ JEKYLL_ROOT = T.let(Jekyll.configuration.source({}), String)
32
+
33
+ sig { params(post: T.untyped).returns(T.nilable(String)) }
34
+ def post_image(post)
35
+ item_id = post.fetch('relative_path')
36
+ Jekyll.logger.info('Post Image:', "Retrieving post image for #{item_id}")
37
+
38
+ image = post.fetch('image') { flickr_primary_photo(post) }
39
+ image = nil unless image && file_exists?(image)
40
+
41
+ if image.nil? && item_id.start_with?('_posts')
42
+ Jekyll.logger.warn('Post Image:', "Missing post image for #{item_id}")
43
+ end
44
+
45
+ image
46
+ end
47
+
48
+ sig { params(post: T.untyped).returns(T::Boolean) }
49
+ def post_has_image(post)
50
+ post_image = post_image(post)
51
+ !!post_image && file_exists?(post_image)
52
+ rescue KeyError
53
+ false
54
+ end
55
+
56
+ sig { params(post: T.untyped).returns(T::Boolean) }
57
+ def show_flickr_gallery(post)
58
+ post.key?('flickr_photoset_id') && !post.fetch('hide_flickr_gallery',
59
+ false) && !post.fetch('hide_post_gallery', false)
60
+ end
61
+
62
+ sig { params(post: T.untyped).returns(T::Boolean) }
63
+ def show_post_gallery(post)
64
+ !post.fetch('hide_post_gallery', false)
65
+ end
66
+
67
+ sig { params(path: String).returns(T::Boolean) }
68
+ private def file_exists?(path)
69
+ full_path = "#{JEKYLL_ROOT}#{path}"
70
+ File.exist?(full_path)
71
+ end
72
+
73
+ sig { params(post: T.untyped).returns(T.nilable(String)) }
74
+ private def flickr_primary_photo(post)
75
+ photoset_id = FlickrHelpers.get_first_photoset_id(post.fetch('flickr_photoset_id'))
76
+ FlickrHelpers.get_photoset_primary_photo(photoset_id)
77
+ rescue KeyError
78
+ nil
79
+ end
80
+ end
81
+ end
82
+
83
+ Liquid::Template.register_filter(Jekyll::PostImage)
84
+
85
+ Jekyll::Hooks.register :posts, :pre_render do |post|
86
+ # Set the image field from Flickr data so jekyll-seo-tag renders the correct meta tags.
87
+ if !post.data.key?('image') && post.data.key?('flickr_photoset_id')
88
+ photoset_id = Jekyll::FlickrHelpers.get_first_photoset_id(post.data.fetch('flickr_photoset_id'))
89
+ image_path = Jekyll::FlickrHelpers.get_photoset_primary_photo(photoset_id)
90
+ post.data['image'] = image_path
91
+ end
92
+ end
metadata ADDED
@@ -0,0 +1,120 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: jekyll-flickr-utils
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Clinton Blackburn
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2022-11-25 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.3'
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: '5.0'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: '4.3'
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: '5.0'
33
+ - !ruby/object:Gem::Dependency
34
+ name: down
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '5.0'
40
+ - - "<"
41
+ - !ruby/object:Gem::Version
42
+ version: '6.0'
43
+ type: :runtime
44
+ prerelease: false
45
+ version_requirements: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: '5.0'
50
+ - - "<"
51
+ - !ruby/object:Gem::Version
52
+ version: '6.0'
53
+ - !ruby/object:Gem::Dependency
54
+ name: flickr
55
+ requirement: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ version: '2.0'
60
+ - - "<"
61
+ - !ruby/object:Gem::Version
62
+ version: '3.0'
63
+ type: :runtime
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: '2.0'
70
+ - - "<"
71
+ - !ruby/object:Gem::Version
72
+ version: '3.0'
73
+ - !ruby/object:Gem::Dependency
74
+ name: sorbet-runtime
75
+ requirement: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ version: '0'
80
+ type: :runtime
81
+ prerelease: false
82
+ version_requirements: !ruby/object:Gem::Requirement
83
+ requirements:
84
+ - - ">="
85
+ - !ruby/object:Gem::Version
86
+ version: '0'
87
+ description:
88
+ email:
89
+ executables: []
90
+ extensions: []
91
+ extra_rdoc_files: []
92
+ files:
93
+ - LICENSE
94
+ - lib/flickr_downloader.rb
95
+ - lib/jekyll-flickr-utils.rb
96
+ - lib/post_image.rb
97
+ homepage:
98
+ licenses:
99
+ - MIT
100
+ metadata: {}
101
+ post_install_message:
102
+ rdoc_options: []
103
+ require_paths:
104
+ - lib
105
+ required_ruby_version: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ version: 2.7.0
110
+ required_rubygems_version: !ruby/object:Gem::Requirement
111
+ requirements:
112
+ - - ">="
113
+ - !ruby/object:Gem::Version
114
+ version: '0'
115
+ requirements: []
116
+ rubygems_version: 3.1.6
117
+ signing_key:
118
+ specification_version: 4
119
+ summary: Download images from Flickr
120
+ test_files: []