etfc 1.0.0

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