flickrage 0.1.1

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.
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+ module Flickrage
3
+ module Worker
4
+ class Download < Base
5
+ def call(image_list)
6
+ raise Flickrage::DownloadError, 'Not enough images for download' if image_list&.size < 1
7
+
8
+ speaker.add_padding
9
+ logger.debug('Downloading process')
10
+ logger.info("Scheduled to download #{image_list.size} images")
11
+
12
+ init_output
13
+
14
+ @spin = spinner(message: 'Downloading')
15
+ files = image_list.images.map do |image|
16
+ Concurrent
17
+ .future(thread_pool) do
18
+ update_spin(spin, title: "Downloading image ID##{image.id}")
19
+ service.run(image)
20
+ end
21
+ .then do |r|
22
+ update_spin(spin, title: "Downloaded image ID##{r.id}")
23
+ r
24
+ end
25
+ .rescue { |_| nil }
26
+ end
27
+
28
+ result = Concurrent.zip(*files).value
29
+ result = result.compact.flatten if result
30
+
31
+ total = result.count(&:downloaded?)
32
+
33
+ if total.positive?
34
+ spin.success
35
+ else
36
+ spin.error('(failed: Not enough images downloaded)')
37
+ raise Flickrage::DownloadError
38
+ end
39
+
40
+ speaker.add_padding
41
+ logger.info("Downloaded #{total} images:")
42
+ speaker.print_table([PRINT_IMAGE_HEADERS_LITE + %w(path downloaded?)] + result.map do |i|
43
+ [i.keyword, i.id, i.local_path, i.downloaded?]
44
+ end)
45
+
46
+ image_list.merge_images result
47
+ ensure
48
+ clean_thread_pool
49
+ spin&.stop
50
+ end
51
+
52
+ private
53
+
54
+ def service
55
+ @service ||= Service::Downloader.new
56
+ end
57
+
58
+ def init_output
59
+ return if validate_output
60
+
61
+ output = speaker.ask('Please enter the path of the output directory:',
62
+ path: true)
63
+
64
+ unless valid_output?(output)
65
+ increment_error_counter(Flickrage::PathError, output)
66
+ return init_output
67
+ end
68
+
69
+ reset_error_counter
70
+
71
+ Flickrage.config.output = output
72
+ end
73
+
74
+ def validate_output
75
+ return false unless Flickrage.config.output
76
+ return true if valid_output?(Flickrage.config.output)
77
+ Flickrage.config.output = nil
78
+ false
79
+ end
80
+
81
+ def valid_output?(value)
82
+ return false unless value
83
+ Dir.exist?(value)
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,85 @@
1
+ # frozen_string_literal: true
2
+ module Flickrage
3
+ module Worker
4
+ class Resize < Base
5
+ MIN_IMAGE_SIZE = 100
6
+ MAX_IMAGE_SIZE = 500
7
+
8
+ def call(image_list)
9
+ raise Flickrage::ResizeError, 'Not enough images for resize' if image_list.downloaded&.size < 1
10
+
11
+ speaker.add_padding
12
+ logger.debug('Resizing process')
13
+
14
+ init_crop_value('width')
15
+ init_crop_value('height')
16
+
17
+ @spin = spinner(message: 'Resizing images')
18
+ files = image_list.downloaded.map do |image|
19
+ Concurrent
20
+ .future(thread_pool) do
21
+ update_spin(spin, title: "Resizing image #{image.id}")
22
+ service.run(image)
23
+ end
24
+ .then do |r|
25
+ update_spin(spin, title: "Resized image #{r.id}")
26
+ r
27
+ end
28
+ .rescue { |_| nil }
29
+ end
30
+
31
+ result = Concurrent.zip(*files).value
32
+ result = result.compact.flatten if result
33
+
34
+ total = result.count(&:resized?)
35
+
36
+ if total > 2
37
+ spin.success
38
+ else
39
+ spin.error('(failed: Not enough images resized)')
40
+ raise Flickrage::ResizeError
41
+ end
42
+
43
+ speaker.add_padding
44
+ logger.info("Resized #{result.count(&:resized?)} images:")
45
+ speaker.print_table([PRINT_IMAGE_HEADERS_LITE + %w(path resized?)] + result.map do |i|
46
+ [i.keyword, i.id, i.resize_path, i.resized?]
47
+ end)
48
+
49
+ image_list.merge_images result
50
+ ensure
51
+ clean_thread_pool
52
+ spin&.stop
53
+ end
54
+
55
+ private
56
+
57
+ def service
58
+ return @service unless @service.nil?
59
+ @service = Service::Resizer.new(Flickrage.config.width, Flickrage.config.height)
60
+ end
61
+
62
+ def init_crop_value(name)
63
+ return true if valid_value?(Flickrage.config[name])
64
+
65
+ value = speaker.ask("Please enter image resize #{name}:")
66
+ value = String(value).to_i
67
+
68
+ unless valid_value?(value)
69
+ increment_error_counter(Flickrage::NumberError,
70
+ "#{value} >= #{MIN_IMAGE_SIZE}, #{value} =< #{MAX_IMAGE_SIZE}")
71
+ return init_crop_value(name)
72
+ end
73
+
74
+ reset_error_counter
75
+
76
+ Flickrage.config[name] = value
77
+ end
78
+
79
+ def valid_value?(value)
80
+ return false unless value
81
+ value >= MIN_IMAGE_SIZE && value <= MAX_IMAGE_SIZE
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,106 @@
1
+ # frozen_string_literal: true
2
+ module Flickrage
3
+ module Worker
4
+ class Search < Base
5
+ include Flickrage::Helpers::Dict
6
+
7
+ SYSTEM_TIMEOUT = 20
8
+
9
+ def call
10
+ keywords = opts['keywords'] || %w()
11
+
12
+ keywords += sample_words(Flickrage.config.max - keywords.size) if keywords.size < Flickrage.config.max
13
+ keys = keywords.join(', ')
14
+
15
+ speaker.add_padding
16
+ logger.debug('Searching process')
17
+ logger.info("Received keywords: [#{keys}]")
18
+
19
+ @spin = spinner(message: 'Searching ')
20
+
21
+ image_list = finder(keywords, spin)
22
+
23
+ if image_list.valid?
24
+ spin.success
25
+ else
26
+ spin.error('(nothing found)')
27
+ raise Flickrage::SearchError
28
+ end
29
+
30
+ speaker.add_padding
31
+ logger.info("Found #{image_list.total} images:")
32
+ speaker.print_table([PRINT_IMAGE_HEADERS] + image_list.images.map do |i|
33
+ [i.keyword, i.id, i.url, i.title, i.width, i.height]
34
+ end)
35
+
36
+ if Flickrage.config.verbose
37
+ logger.info('Not found keywords:')
38
+ speaker.print_table(image_list.not_founds.map do |word|
39
+ [word]
40
+ end)
41
+ end
42
+
43
+ image_list
44
+ ensure
45
+ clean_thread_pool
46
+ spin&.stop
47
+ end
48
+
49
+ private
50
+
51
+ def time
52
+ @time ||= Time.now.to_i
53
+ end
54
+
55
+ def timeout?
56
+ (Time.now.to_i - time) > SYSTEM_TIMEOUT
57
+ end
58
+
59
+ def service
60
+ @service ||= Service::Search.new
61
+ end
62
+
63
+ def finder(keywords, spin, image_list = nil)
64
+ files = keywords.map do |k|
65
+ Concurrent
66
+ .future(thread_pool) do
67
+ update_spin(spin, title: "Searching (keyword: #{k})")
68
+ service.run(k)
69
+ end
70
+ .then do |r|
71
+ update_spin(spin, title: "Searching (found image ID##{r.id})") if r
72
+ r
73
+ end
74
+ .rescue { |_| nil }
75
+ end
76
+
77
+ result = Concurrent.zip(*files).value
78
+ result = result.compact.flatten if result
79
+
80
+ if image_list
81
+ image_list.combine(result)
82
+ image_list.clean
83
+ else
84
+ image_list = Flickrage::Entity::ImageList.new(images: result,
85
+ total: result.size)
86
+ end
87
+
88
+ return image_list if image_list.valid?
89
+
90
+ fail_on_timeout
91
+ success_keywords = result.map(&:keyword)
92
+ not_founds = keywords - success_keywords
93
+ image_list.merge_not_founds(not_founds)
94
+
95
+ keywords = sample_words_strict(not_founds&.size || 0, except: image_list.not_founds)
96
+ return image_list if keywords.size.zero?
97
+
98
+ finder(keywords, spin, image_list)
99
+ end
100
+
101
+ def fail_on_timeout
102
+ raise Flickrage::SystemTimeout if timeout?
103
+ end
104
+ end
105
+ end
106
+ end
File without changes
File without changes
metadata ADDED
@@ -0,0 +1,322 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: flickrage
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Alexander Merkulov
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-08-08 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: thor
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.19'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0.19'
27
+ - !ruby/object:Gem::Dependency
28
+ name: flickraw
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 0.9.9
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 0.9.9
41
+ - !ruby/object:Gem::Dependency
42
+ name: mini_magick
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 4.5.1
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 4.5.1
55
+ - !ruby/object:Gem::Dependency
56
+ name: dry-configurable
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 0.1.7
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 0.1.7
69
+ - !ruby/object:Gem::Dependency
70
+ name: dry-types
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 0.8.1
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 0.8.1
83
+ - !ruby/object:Gem::Dependency
84
+ name: concurrent-ruby-edge
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: tty-spinner
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: 0.3.0
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: 0.3.0
111
+ - !ruby/object:Gem::Dependency
112
+ name: pastel
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: 0.6.0
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: 0.6.0
125
+ - !ruby/object:Gem::Dependency
126
+ name: bundler
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '1.12'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '1.12'
139
+ - !ruby/object:Gem::Dependency
140
+ name: rake
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - "~>"
144
+ - !ruby/object:Gem::Version
145
+ version: '10.0'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - "~>"
151
+ - !ruby/object:Gem::Version
152
+ version: '10.0'
153
+ - !ruby/object:Gem::Dependency
154
+ name: json
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - "~>"
158
+ - !ruby/object:Gem::Version
159
+ version: 1.8.1
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - "~>"
165
+ - !ruby/object:Gem::Version
166
+ version: 1.8.1
167
+ - !ruby/object:Gem::Dependency
168
+ name: rspec
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - "~>"
172
+ - !ruby/object:Gem::Version
173
+ version: 3.5.0
174
+ type: :development
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - "~>"
179
+ - !ruby/object:Gem::Version
180
+ version: 3.5.0
181
+ - !ruby/object:Gem::Dependency
182
+ name: rspec-core
183
+ requirement: !ruby/object:Gem::Requirement
184
+ requirements:
185
+ - - "~>"
186
+ - !ruby/object:Gem::Version
187
+ version: 3.5.0
188
+ type: :development
189
+ prerelease: false
190
+ version_requirements: !ruby/object:Gem::Requirement
191
+ requirements:
192
+ - - "~>"
193
+ - !ruby/object:Gem::Version
194
+ version: 3.5.0
195
+ - !ruby/object:Gem::Dependency
196
+ name: rspec-expectations
197
+ requirement: !ruby/object:Gem::Requirement
198
+ requirements:
199
+ - - "~>"
200
+ - !ruby/object:Gem::Version
201
+ version: 3.5.0
202
+ type: :development
203
+ prerelease: false
204
+ version_requirements: !ruby/object:Gem::Requirement
205
+ requirements:
206
+ - - "~>"
207
+ - !ruby/object:Gem::Version
208
+ version: 3.5.0
209
+ - !ruby/object:Gem::Dependency
210
+ name: rspec-mocks
211
+ requirement: !ruby/object:Gem::Requirement
212
+ requirements:
213
+ - - "~>"
214
+ - !ruby/object:Gem::Version
215
+ version: 3.5.0
216
+ type: :development
217
+ prerelease: false
218
+ version_requirements: !ruby/object:Gem::Requirement
219
+ requirements:
220
+ - - "~>"
221
+ - !ruby/object:Gem::Version
222
+ version: 3.5.0
223
+ - !ruby/object:Gem::Dependency
224
+ name: webmock
225
+ requirement: !ruby/object:Gem::Requirement
226
+ requirements:
227
+ - - "~>"
228
+ - !ruby/object:Gem::Version
229
+ version: 2.1.0
230
+ type: :development
231
+ prerelease: false
232
+ version_requirements: !ruby/object:Gem::Requirement
233
+ requirements:
234
+ - - "~>"
235
+ - !ruby/object:Gem::Version
236
+ version: 2.1.0
237
+ - !ruby/object:Gem::Dependency
238
+ name: coveralls
239
+ requirement: !ruby/object:Gem::Requirement
240
+ requirements:
241
+ - - "~>"
242
+ - !ruby/object:Gem::Version
243
+ version: 0.8.15
244
+ type: :development
245
+ prerelease: false
246
+ version_requirements: !ruby/object:Gem::Requirement
247
+ requirements:
248
+ - - "~>"
249
+ - !ruby/object:Gem::Version
250
+ version: 0.8.15
251
+ description: Little tool with idea of downloading random top-n pictures from the Flickr,
252
+ resize it properly & build collage from them!
253
+ email:
254
+ - sasha@merqlove.ru
255
+ executables:
256
+ - console
257
+ - flickrage
258
+ - setup
259
+ extensions: []
260
+ extra_rdoc_files: []
261
+ files:
262
+ - ".gitignore"
263
+ - ".rspec"
264
+ - ".rubocop.yml"
265
+ - ".ruby-version"
266
+ - ".travis.yml"
267
+ - Gemfile
268
+ - LICENSE.txt
269
+ - README.md
270
+ - Rakefile
271
+ - bin/console
272
+ - bin/flickrage
273
+ - bin/setup
274
+ - flickrage.gemspec
275
+ - lib/flickrage.rb
276
+ - lib/flickrage/cli.rb
277
+ - lib/flickrage/entity.rb
278
+ - lib/flickrage/entity/image.rb
279
+ - lib/flickrage/entity/image_list.rb
280
+ - lib/flickrage/helpers.rb
281
+ - lib/flickrage/log.rb
282
+ - lib/flickrage/pipeline.rb
283
+ - lib/flickrage/service.rb
284
+ - lib/flickrage/service/composer.rb
285
+ - lib/flickrage/service/downloader.rb
286
+ - lib/flickrage/service/resizer.rb
287
+ - lib/flickrage/service/search.rb
288
+ - lib/flickrage/types.rb
289
+ - lib/flickrage/version.rb
290
+ - lib/flickrage/worker.rb
291
+ - lib/flickrage/worker/base.rb
292
+ - lib/flickrage/worker/compose.rb
293
+ - lib/flickrage/worker/download.rb
294
+ - lib/flickrage/worker/resize.rb
295
+ - lib/flickrage/worker/search.rb
296
+ - log/.keep
297
+ - tmp/.keep
298
+ homepage: https://github.com/merqlove/flickrage
299
+ licenses:
300
+ - MIT
301
+ metadata: {}
302
+ post_install_message:
303
+ rdoc_options: []
304
+ require_paths:
305
+ - lib
306
+ required_ruby_version: !ruby/object:Gem::Requirement
307
+ requirements:
308
+ - - ">="
309
+ - !ruby/object:Gem::Version
310
+ version: 2.3.0
311
+ required_rubygems_version: !ruby/object:Gem::Requirement
312
+ requirements:
313
+ - - ">="
314
+ - !ruby/object:Gem::Version
315
+ version: '0'
316
+ requirements: []
317
+ rubyforge_project:
318
+ rubygems_version: 2.5.1
319
+ signing_key:
320
+ specification_version: 4
321
+ summary: Another one Flickr collage maker.
322
+ test_files: []