etfc 1.0.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.
data/lib/etfc/cli.rb ADDED
@@ -0,0 +1,32 @@
1
+ require 'thor'
2
+ require 'etfc'
3
+
4
+ module ETFC
5
+ class CLI < Thor
6
+ desc 'collage [filename]', 'create a collage for keywords'
7
+ long_desc <<-LONG
8
+ ETFC is your friendly flickr collage creator.
9
+
10
+ You give it a few keywords like 'butter', 'rick sanchez' and 'purpose';
11
+ ETFC will then go and fetch the most interesting related pictures from
12
+ flickr and create a collage of 5x2 images. You can supply as many keywords
13
+ as you wish, however only ten images will be displayed in the collage. If
14
+ you supply less than ten words, random words will be taken from the
15
+ dictionary.
16
+
17
+ Example usage:
18
+
19
+ etfc collage best_collage_ever.jpg --keywords=butter 'rick sanchez' purpose
20
+ LONG
21
+ option :keywords, type: :array, required: true
22
+ def collage(result = 'collage.jpg')
23
+ ETFC::Runner.run(options[:keywords], result)
24
+ rescue SocketError, Timeout::Error, Errno::EINVAL, Errno::ECONNRESET,
25
+ EOFError, Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError,
26
+ Net::ProtocolError, Errno::ECONNREFUSED
27
+ puts 'There was a problem with the network connection. Please check ' \
28
+ 'your network and try again.'
29
+ exit 1
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,35 @@
1
+ require 'rmagick'
2
+
3
+ module ETFC
4
+ module Collage
5
+ module_function
6
+
7
+ # Public: Creates a collage of 10 images in a 5x2 grid
8
+ #
9
+ # imgs - array of 10 images (paths to the images)
10
+ # output - name of the resulting collage
11
+ #
12
+ # Examples:
13
+ #
14
+ # create(['1.jpg', '2.jpg', ...], 'collage.jpg')
15
+ # #=> 'Magick::ImageList...'
16
+ #
17
+ # Returns Magick::ImageList, can be discarded. The resulting collage will
18
+ # be written to the provided file name.
19
+ def create(imgs, output)
20
+ list = Magick::ImageList.new
21
+ imgs.each do |img|
22
+ list << Magick::Image.read(img)[0]
23
+ end
24
+
25
+ # https://rmagick.github.io/ilist.html#montage
26
+ montage = list.montage do
27
+ self.geometry = Magick::Geometry.new(300, 300)
28
+ self.tile = '5x2'
29
+ self.stroke = 'transparent'
30
+ end
31
+
32
+ montage.write(output)
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,59 @@
1
+ module ETFC
2
+ class Dictionary
3
+ def initialize
4
+ init
5
+ end
6
+
7
+ # Private: Memorize 20 words from the system dictionary
8
+ def init
9
+ @words = dictionary.sample(20).map(&:strip)
10
+ end
11
+
12
+ # Public: Returns a random word
13
+ #
14
+ # Example:
15
+ #
16
+ # random
17
+ # #=> 'mephitical'
18
+ #
19
+ # Returns a random word (String)
20
+ def random
21
+ init if @words.empty?
22
+
23
+ @words.pop
24
+ end
25
+
26
+ # Private: Returns a dictionary
27
+ #
28
+ # Checks for a words file and fallsback to a minimal dictionary
29
+ # https://en.wikipedia.org/wiki/Words_(Unix)
30
+ def dictionary
31
+ if File.file?('/usr/share/dict/words')
32
+ words_file('/usr/share/dict/words')
33
+ elsif File.file?('/usr/dict/words')
34
+ words_file('/usr/dict/words')
35
+ else
36
+ %w(
37
+ this is a jury rigged dictionary banana monkey apple pear peach
38
+ computers are so great robot dance
39
+ )
40
+ end
41
+ end
42
+
43
+ # Private: Retrieve all 3 to 9 letter words from a provided file
44
+ #
45
+ # words - words file
46
+ #
47
+ # Example:
48
+ #
49
+ # words_file('/usr/share/dict/words')
50
+ # #=> ['banana', ...]
51
+ #
52
+ # Retruns a list of words
53
+ def words_file(words)
54
+ File.read(words).lines.select do |l|
55
+ (3..9).cover?(l.strip.size)
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,38 @@
1
+ require 'flickraw'
2
+
3
+ module ETFC
4
+ module Flickr
5
+ module_function
6
+
7
+ # Flickr requires authentication for API interaction
8
+ FlickRaw.api_key = ETFC::FLICKR_API_KEY
9
+ FlickRaw.shared_secret = ETFC::FLICKR_SHARED_SECRET
10
+
11
+ # Public: Search for the most interesting image on flickr for a keyword
12
+ #
13
+ # keyword - the keyword to search for
14
+ #
15
+ # Examples:
16
+ #
17
+ # search('something interesting')
18
+ # #=> 'http://farm9.staticflickr.com/1234/11111111111_2222222222_b.jpg'
19
+ #
20
+ # search('something that totally wont match')
21
+ # #=> nil
22
+ #
23
+ # Returns a String (URL) for an image or `nil` if no image was found
24
+ def search(keyword)
25
+ # See https://www.flickr.com/services/api/flickr.photos.search.html /
26
+ # http://hanklords.github.io/flickraw/FlickRaw/Flickr/Photos.html#method-i-search
27
+ results = flickr.photos.search(sort: 'interestingness-desc',
28
+ text: keyword,
29
+ page: 1,
30
+ per_page: 1,
31
+ media: 'photos',
32
+ content_type: 1,
33
+ extras: 'url_l')
34
+
35
+ results.first['url_l'] if results.size > 0
36
+ end
37
+ end
38
+ end
data/lib/etfc/image.rb ADDED
@@ -0,0 +1,45 @@
1
+ require 'open-uri'
2
+ require 'rmagick'
3
+
4
+ module ETFC
5
+ module Image
6
+ module_function
7
+
8
+ # Public: Download a file and save it to the temporary folder
9
+ #
10
+ # url - URL of the file that shall be downloaded
11
+ # name - name for the to be downloaded file
12
+ #
13
+ # Examples:
14
+ #
15
+ # download('http://example.com/abc.jpg', '123.jpg')
16
+ # #=> "/var/tmp/111/222/123.jpg"
17
+ #
18
+ # Returns String with the location of the downloaded file
19
+ def download(url, name)
20
+ path = "#{ETFC::TMP_DIR}/#{name}"
21
+ download = open(url)
22
+ IO.copy_stream(download, path)
23
+ path
24
+ end
25
+
26
+ # Public: Crop an image destructively
27
+ #
28
+ # image - path of the image that shall be cropped
29
+ # width - OPTIONAL width, defaults to 300
30
+ # height - OPTIONAL height, defaults to 300
31
+ #
32
+ # Examples:
33
+ #
34
+ # crop('/abc/123.jpg')
35
+ # #=> #<Magick::Image:70103481521220> => /abc/123.jpg JPEG
36
+ # 800x600=>300x300 800x600+250+150 DirectClass 8-bit
37
+ #
38
+ # Returns Magick::Image with the crop transformation queued
39
+ def crop(image, width = 300, height = 300)
40
+ img = Magick::Image.read(image)[0]
41
+ img.crop!(Magick::CenterGravity, width, height)
42
+ img.write(image)
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,65 @@
1
+ module ETFC
2
+ module Runner
3
+ module_function
4
+
5
+ # Public: create a collage of images found on flickr for a list of keywords
6
+ #
7
+ # keywords - array of keywords (up to 10)
8
+ # output - name of the resulting collage
9
+ #
10
+ # Example:
11
+ #
12
+ # run(['banana', 'monkey'], 'collage.jpg')
13
+ # # the collage will be saved to collage.jpg in your current directory
14
+ #
15
+ # Returns Magick::ImageList; can be discarded as the collage will be saved
16
+ # to the supplied file name on disk
17
+ def run(keywords, output)
18
+ check_api_credentials
19
+
20
+ images_urls = ten_img_urls(keywords)
21
+
22
+ images_urls.each_with_index do |url, idx|
23
+ Image.download(url, "#{idx}.jpg")
24
+ end
25
+
26
+ img_list = Dir[TMP_DIR + '/**.jpg']
27
+
28
+ img_list.each { |img| Image.crop(img) }
29
+
30
+ Collage.create(img_list, output)
31
+ end
32
+
33
+ # Public: get ten image urls for keywords; if less than 10 keywords are
34
+ # supplied, random keywords will be selected from the system dictionary
35
+ #
36
+ # keywords - array of keywords (up to 10)
37
+ #
38
+ # Example:
39
+ #
40
+ # ten_img_urls(['banana'])
41
+ # #=> ['http://example.com/123.jpog', '...]
42
+ #
43
+ # Returns an array of ten image URLs
44
+ def ten_img_urls(keywords)
45
+ images = keywords.map do |keyword|
46
+ Flickr.search(keyword)
47
+ end.reject(&:nil?)
48
+
49
+ dict = Dictionary.new
50
+ while images.length < 10
51
+ result = Flickr.search(dict.random)
52
+ images << result unless result.nil?
53
+ end
54
+
55
+ images.take(10)
56
+ end
57
+
58
+ def check_api_credentials
59
+ return if ETFC::FLICKR_API_KEY || ETFC::FLICKR_SHARED_SECRET
60
+ puts 'Please set FLICKR_API_KEY and FLICKR_SHARED_SECRET as ' \
61
+ 'environment variables'
62
+ exit 1
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,3 @@
1
+ module ETFC
2
+ VERSION = '1.0.0'.freeze
3
+ end
data/lib/etfc.rb ADDED
@@ -0,0 +1,15 @@
1
+ require 'tmpdir'
2
+
3
+ module ETFC
4
+ FLICKR_API_KEY = ENV['FLICKR_API_KEY']
5
+ FLICKR_SHARED_SECRET = ENV['FLICKR_SHARED_SECRET']
6
+
7
+ TMP_DIR = Dir.mktmpdir
8
+ end
9
+
10
+ require 'etfc/version'
11
+ require 'etfc/flickr'
12
+ require 'etfc/image'
13
+ require 'etfc/collage'
14
+ require 'etfc/dictionary'
15
+ require 'etfc/runner'
Binary file
metadata ADDED
@@ -0,0 +1,223 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: etfc
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Maximilian Haack
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2017-01-03 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: flickraw
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 0.9.9
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 0.9.9
27
+ - !ruby/object:Gem::Dependency
28
+ name: rmagick
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 2.16.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 2.16.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: thor
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 0.19.4
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 0.19.4
55
+ - !ruby/object:Gem::Dependency
56
+ name: bundler
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.13'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.13'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '10.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '10.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rspec
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '3.0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '3.0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rubocop
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: 0.46.0
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: 0.46.0
111
+ - !ruby/object:Gem::Dependency
112
+ name: simplecov
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: 0.12.0
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: 0.12.0
125
+ - !ruby/object:Gem::Dependency
126
+ name: webmock
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: 2.3.1
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: 2.3.1
139
+ - !ruby/object:Gem::Dependency
140
+ name: vcr
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - "~>"
144
+ - !ruby/object:Gem::Version
145
+ version: 3.0.3
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - "~>"
151
+ - !ruby/object:Gem::Version
152
+ version: 3.0.3
153
+ description: ETFC is a tool to create collages from flickr images based upon user
154
+ provided keywords.
155
+ email:
156
+ - mxhaack@gmail.com
157
+ executables:
158
+ - etfc
159
+ extensions: []
160
+ extra_rdoc_files: []
161
+ files:
162
+ - ".gitignore"
163
+ - ".rspec"
164
+ - ".rubocop.yml"
165
+ - ".travis.yml"
166
+ - CODE_OF_CONDUCT.md
167
+ - Gemfile
168
+ - LICENSE.txt
169
+ - README.md
170
+ - Rakefile
171
+ - bin/console
172
+ - bin/setup
173
+ - etfc.gemspec
174
+ - exe/etfc
175
+ - fixtures/images/1.jpg
176
+ - fixtures/images/10.jpg
177
+ - fixtures/images/2.jpg
178
+ - fixtures/images/3.jpg
179
+ - fixtures/images/4.jpg
180
+ - fixtures/images/5.jpg
181
+ - fixtures/images/6.jpg
182
+ - fixtures/images/7.jpg
183
+ - fixtures/images/8.jpg
184
+ - fixtures/images/9.jpg
185
+ - fixtures/vcr_cassettes/collage.yml
186
+ - fixtures/vcr_cassettes/download_blank_image.yml
187
+ - fixtures/vcr_cassettes/flickr_search_match.yml
188
+ - fixtures/vcr_cassettes/flickr_search_no_match.yml
189
+ - fixtures/vcr_cassettes/ten_images.yml
190
+ - lib/etfc.rb
191
+ - lib/etfc/cli.rb
192
+ - lib/etfc/collage.rb
193
+ - lib/etfc/dictionary.rb
194
+ - lib/etfc/flickr.rb
195
+ - lib/etfc/image.rb
196
+ - lib/etfc/runner.rb
197
+ - lib/etfc/version.rb
198
+ - sample_collage.jpg
199
+ homepage: https://www.github.com/coffeejunk/etfc
200
+ licenses:
201
+ - MIT
202
+ metadata: {}
203
+ post_install_message:
204
+ rdoc_options: []
205
+ require_paths:
206
+ - lib
207
+ required_ruby_version: !ruby/object:Gem::Requirement
208
+ requirements:
209
+ - - ">="
210
+ - !ruby/object:Gem::Version
211
+ version: '0'
212
+ required_rubygems_version: !ruby/object:Gem::Requirement
213
+ requirements:
214
+ - - ">="
215
+ - !ruby/object:Gem::Version
216
+ version: '0'
217
+ requirements: []
218
+ rubyforge_project:
219
+ rubygems_version: 2.5.2
220
+ signing_key:
221
+ specification_version: 4
222
+ summary: A flickr collage maker
223
+ test_files: []