jekyll-flickr-utils 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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: []